标签:break 条件 val set 复杂 use 答案 getch using
令人震惊的一点在于,像我一样无脑puts("-1")的,居然有40分。
然而经大佬指点,在每一位上求填某个数对答案的贡献是可以直接计算出来的。
具体实现是通过dp预处理。定义dp[i][j]为1~i的排列中逆序对数为j的方案数。
对于状态转移,枚举新的一位填上了第几大的数。
于是我们有 dp[i][j]=∑dp[i-1][k] (j-i+1<=k<=j)。对此可以用前缀和进行优化。
因此预处理的时间复杂度为O(n*k)。
然后,我们一位位地计算答案。设当前为第i位,目前选取的数是j,需要有k组逆序对,j在可选数的字典序中排第val[j]个,那么选取j的贡献就是dp[n-i][k-val[j]+1]。
因此,我们可以当前位按字典序从小到大进行枚举,如果其贡献值大于等于p则必然选取这个数,否则把p减去其贡献值。
具体实现时用used数组记录每个数是否可选,并通过差分优化val数组的计算。
时间复杂度O(n*k+n^2)。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int N=1010,K=10010,INF=1e12; 5 int dp[N][K],g[N][N],val[N],ans[N],num[N]; 6 bool used[N]; 7 int n,k,p; 8 int get_dp(int x,int y) 9 { 10 if(y==0) return dp[x][y]; 11 return dp[x][y]-dp[x][y-1]; 12 } 13 signed main() 14 { 15 scanf("%lld%lld%lld",&n,&k,&p); 16 for(int i=1;i<=n;i++) 17 for(int j=1;j<=n;j++) scanf("%lld",&g[i][j]); 18 for(int i=0;i<=k;i++) dp[1][i]=1,dp[0][i]=i+1; 19 for(int i=2;i<=n;i++) 20 { 21 for(int j=0;j<=k;j++) 22 { 23 if(j-i<0) dp[i][j]+=dp[i-1][j]; 24 else dp[i][j]+=dp[i-1][j]-dp[i-1][j-i]; 25 dp[i][j]=min(dp[i][j],INF); 26 dp[i][j]+=dp[i][j-1]; 27 } 28 } 29 if(get_dp(n,k)<p) return 0*puts("-1"); 30 int le=k,now=p; 31 for(int i=1;i<=n;i++) 32 { 33 memset(val,0,sizeof(val)); 34 for(int j=1;j<=n;j++) if(used[j]) val[j+1]--; 35 for(int j=1;j<=n;j++) val[j]+=val[j-1]; 36 for(int j=1;j<=n;j++) val[j]+=j-1; 37 for(int j=1;j<=n;j++) num[j]=val[g[i][j]]; 38 for(int j=1;j<=n;j++) if(!used[g[i][j]]&&num[j]<=le) 39 { 40 if(get_dp(n-i,le-num[j])>=now) 41 { 42 le-=num[j]; 43 ans[i]=g[i][j]; 44 used[g[i][j]]=1; 45 break; 46 } 47 else now-=get_dp(n-i,le-num[j]); 48 } 49 } 50 for(int i=1;i<=n;i++) printf("%lld ",ans[i]); 51 return 0*printf("\n"); 52 }
非常令人在意的是每条边边权只为1或2的性质。我由此得出了每个小时(除了最后一个)必然走d或d-1的长度。于是我由此确定答案的上下界,random了一下,骗了60分。
然而那还需要卡常的正解并没有用到这个性质。
注意到路径是可以拓展的,也就是说,记top[i][j]为i结点像根节点最远能到达的点,那么top[i][j]=top[top[i][k]][j-k] (0<=k<=j)。
以及如下这个结论:对于路径的两个端点a,b,以及它们的LCA z,我们可以让a和b尽可能地向z移动,最后如果a,b重合则得到答案,如果a,b的距离小于等于d则让ans+1,否则让ans+2,然后这就是最优解。
对于这个结论的正确性,假设有更优解,那么记它跨越z的那个小时是从c到d(c是初始a的祖先),那么要么最后a(b)一定与c(d)重合或在其上面,就是其中一个符合这个条件,另一个可以通过不多于1次的移动来满足这一条件。而先前的每一次移动都是贪心最优的,因此这一定是最优解。
于是我们可以充分地利用倍增的思想,对上述算法进行优化。
具体就是记录top[i][j]表示从i向上移动2^j步所最远能到达的点。(求top[i][0]可以用倍增或二分,求LCA我又用了一个倍增)。
时间复杂度O(n*logn)。(然而我还要用快读优化)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=500010,MAXPOS=19; 4 int n,d,q; 5 int fa[N][MAXPOS+1],dis[N][MAXPOS+1],top[N][MAXPOS+1],deep[N]; 6 void prework() 7 { 8 int le,pos; 9 for(int i=1;i<=n;i++) 10 { 11 for(int j=1;j<=MAXPOS;j++) 12 { 13 fa[i][j]=fa[fa[i][j-1]][j-1]; 14 dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1]; 15 } 16 le=d;pos=i; 17 for(int j=MAXPOS;j>=0;j--) 18 { 19 if(dis[pos][j]<=le) le-=dis[pos][j],pos=fa[pos][j]; 20 } 21 top[i][0]=pos; 22 for(int j=1;j<=MAXPOS;j++) 23 { 24 top[i][j]=top[top[i][j-1]][j-1]; 25 } 26 } 27 } 28 int lca(int a,int b) 29 { 30 if(deep[a]<deep[b]) swap(a,b); 31 for(int i=MAXPOS;i>=0;i--) 32 { 33 if(deep[fa[a][i]]>=deep[b]) a=fa[a][i]; 34 } 35 if(a==b) return a; 36 for(int i=MAXPOS;i>=0;i--) 37 { 38 if(fa[a][i]!=fa[b][i]) 39 a=fa[a][i],b=fa[b][i]; 40 } 41 return fa[a][0]; 42 } 43 int main() 44 { 45 int x,y,ans,z; 46 scanf("%d%d%d",&n,&d,&q); 47 deep[1]=1; 48 for(int i=2;i<=n;i++) 49 { 50 scanf("%d%d",&x,&y); 51 fa[i][0]=x; 52 dis[i][0]=y; 53 deep[i]=deep[x]+1; 54 } 55 prework(); 56 for(int i=1;i<=q;i++) 57 { 58 ans=0; 59 scanf("%d%d",&x,&y); 60 z=lca(x,y); 61 for(int j=MAXPOS;j>=0;j--) 62 { 63 if(deep[top[x][j]]>deep[z]) x=top[x][j],ans+=(1<<j); 64 if(deep[top[y][j]]>deep[z]) y=top[y][j],ans+=(1<<j); 65 } 66 if(x!=y) 67 { 68 if(dis[x][MAXPOS]+dis[y][MAXPOS]-2*dis[z][MAXPOS]<=d) ans++; 69 else ans+=2; 70 } 71 printf("%d\n",ans); 72 } 73 return 0; 74 }
又是喜闻乐见的水题了,然而带有码农属性。
(用gets读入导致我爆30,而另一个用gets的同学爆0了。)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=100010; 4 char pic[8][N*7]; 5 int num,sum,n,ans[N]; 6 int main() 7 { 8 scanf("%d",&n); 9 getchar(); 10 for(int i=1;i<=6;i++) 11 { 12 for(int j=1;j<=n;j++) 13 scanf("%s",pic[i]+7*j-6); 14 } 15 int pos=0,x,y; 16 for(int i=1;i<=n;i++,pos+=7) 17 { 18 num=0; 19 for(int j=1;j<=6;j++) 20 { 21 for(int k=1;k<=6;k++) 22 { 23 if(pic[j][k+pos]==‘.‘) 24 { 25 if(num==0) y=k+pos; 26 num++; 27 } 28 } 29 if(num>0) 30 { 31 x=j; 32 break; 33 } 34 } 35 if(pic[x][y+1]==‘.‘&&pic[x][y+2]==‘.‘) 36 { 37 if(pic[x+1][y]==‘.‘&&pic[x+1][y+2]==‘.‘) 38 { 39 if(pic[x+2][y+1]==‘#‘) ans[i]=0; 40 else if(pic[x+3][y]==‘#‘) ans[i]=9; 41 else ans[i]=8; 42 continue; 43 } 44 if(pic[x+4][y]==‘#‘) 45 { 46 ans[i]=7; 47 continue; 48 } 49 if(pic[x+3][y]==‘.‘) 50 { 51 if(pic[x+3][y+1]==‘#‘&&pic[x+3][y+2]==‘.‘) ans[i]=6; 52 else ans[i]=2; 53 continue; 54 } 55 if(pic[x+1][y]==‘#‘) ans[i]=3; 56 else ans[i]=5; 57 } 58 else 59 { 60 if(pic[x+1][y-1]==‘.‘) ans[i]=4; 61 else ans[i]=1; 62 continue; 63 } 64 } 65 for(int i=1;i<=n;i++) 66 printf("%d ",ans[i]); 67 return 0; 68 }
小结:靠rp骗了70分,然后又莫名被坑了70分。
标签:break 条件 val set 复杂 use 答案 getch using
原文地址:http://www.cnblogs.com/cly-none/p/7616142.html