P1335 [NOI2013] 小Q的修炼 题解

· · 题解

对于此题,我认为的难点是观察数据并猜测性质和读入操作。

我隔一会就思考这个字符串读起来怎么这么麻烦啊。

首先可以发现,这个所有的选择都之后往后走,就是个 topo 图。

task1,2,3

观察到数据有形如:

s x x+11

v 3 + c y

v 4 + c y

v 5 + c y

v 6 + c y

v 7 + c y

v 8 + c y

v 9 + c y

v 10 + c y

v 11 + c y

v 12 + c y

之类的东西,而且每个这样的循环后面有一个这样的东西。


i v 3 c 0 43 45

v 1 - v 3

i c 0 c 1 46 0

v 1 + v 3

v 3 - v 3

i v 4 c 0 48 50

v 1 - v 4

i c 0 c 1 51 0

v 1 + v 4

v 4 - v 4

发现这个就是把正的数据加到 1 上,负的不变并清空。

这样就没法贪心或者 dp 了。

然后我们又发现循环长度很小,直接爆搜就行了。

task4,5,6

最开始对变量 2 有一个正的初值。

然后:


s 5 8

v 2 - c 10

v 1 + c 22750

i c 0 c 1 8 0

i v 2 c 9 9 10

i c 0 c 1 14 0

循环节长这样。

每次如果选择,会有一些跳转之类的。

发现 2 的初值比较小而且后面只会减。

考虑 dp 。

然后模拟转移,记录路径就可以了。 ``` #include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::max; const int N=6010; int n,m; char op[5],t0[10],t1[10]; int x0,x1,x2,x2; struct koito_yuu { int op,a,b,c; koito_yuu(){} koito_yuu(int Op,int A,int B,int C){op=Op,a=A,b=B,c=C;} }yuu[N]; struct node { int i,j,cho; node(){} node(int I,int J,int Cho){i=I,j=J,cho=Cho;} }pre[N][5010]; ll dp[N][5010]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",op); if(op[0]=='s') { tim[++k]=i; scanf("%d%d",&x1,&x2); yuu[i]=koito_yuu(1,x1,x2,0); } else if(op[0]=='v') { scanf("%d%s%s%d",&x0,t0,t1,&x1); yuu[i]=koito_yuu(2,x0,t1[0]=='+'?x1:-x1,0); } else { scanf("%s%d%s%d%d%d",t0,&x0,t1,&x1,&x2,&x3); if(t0[0]=='c'&&t1[0]=='c') yuu[i]=koito_yuu(3,x2,0,0); else if(t0[0]=='v'&&t1[0]=='c') yuu[i]=koito_yuu(4,x1,x2,x3); } } memset(dp,-0x3f,sizeof dp); int inf=dp[0][0]; dp[0][0]=0; for(int i=1;i<=n;i++) for(int j=0;j<=5000;j++) if(dp[i-1][j]!=inf) { if(yuu[i].op==1) { int a=yuu[i].a,b=yuu[i].b; if(dp[a][j]<dp[i][j]) { pre[a][j]=node(i,j,1); dp[a][j]=dp[i][j]; } if(dp[b][j]<dp[i][j]) { pre[b][j]=node(i,j,2); dp[b][j]=dp[i][j]; } } else if(yuu[i].op==2) { int a=yuu[i].a,b=yuu[i].b; if(a==1) { if(dp[i+1][j]<dp[i][j]+b) { pre[i+1][j]=node(i,j,0); dp[i+1][j]=dp[i][j]+b; } } else { int t=max(0,j+b); if(dp[i+1][t]<dp[i][j]) { pre[i+1][t]=node(i,j,0); dp[i+1][t]=dp[i][j]; } } } else if(yuu[i].op==3) { int a=yuu[i].a; if(dp[a][j]<dp[i][j]) { pre[a][j]=node(i,j,0); dp[a][j]=dp[i][j]; } } else { int a=yuu[i].a,b=yuu[i].b,c=yuu[i].c; if(j<a) { if(dp[b][j]<dp[i][j]) { pre[b][j]=node(i,j,0); dp[b][j]=dp[i][j]; } } else { if(dp[c][j]<dp[i][j]) { pre[c][j]=node(i,j,0); dp[c][j]=dp[i][j]; } } } } int tj=0; for(int i=1;i<=5000;i++) if(dp[n+1][tj]<dp[n+1][i]) tj=i; int ti=n+1,cnt=0; while(ti) { int tx=ti,ty=tj; ti=pre[tx][ty].i,tj=pre[tx][ty].j; if(pre[tx][ty].cho) ans[++cnt]=pre[tx][ty].cho; } for(int i=cnt;i;i--) printf("%d\n",ans[i]); return 0; } ``` #### task7,8,9,10 这四个点就是前面的结合。 最开始的时候有 $2$ 控制跳转的东西。 中间就是 $1,2,3$ 点的循环。 差不多 $175$ 行一个周期。 对周期压变量 $2$ 的长度 $dp$ ,每个周期里面爆搜。 说起来简单,写起来还是很麻烦的。 ### Code ``` //我这个代码要把in的最后几行删掉。 #include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::max; using std::min; const int N=35010; int n,m; char op[5],t0[10],t1[10]; int x0,x1,x2,x3; struct koito_yuu { int op,a,b,c; koito_yuu(){} koito_yuu(int Op,int A,int B,int C){op=Op,a=A,b=B,c=C;} }yuu[N]; struct node { int i,j,cho; node(){} node(int I,int J,int Cho){i=I,j=J,cho=Cho;} }pre[N][1010]; ll dp[N][1010]; int Id[N],as[N][20],yuy[20][20],ct[N]; ll solve(int id,int L,int R) { int n=0; for(int i=L;i<=R;i++) { if(yuu[i].op==1) ++n,yuy[n][0]=0; if(yuu[i].op==2) yuy[n][++yuy[n][0]]=yuu[i].b; if(yuu[i].op==3) break; } ll mx=-(1ll<<52); ct[id]=n; for(int s=0;s<1<<n;s++) { ll sum[20]={}; for(int i=1;i<=n;i++) if(s>>i-1&1) { for(int j=1;j<=10;j++) sum[j]+=yuy[i][j]; } ll su=0; for(int i=1;i<=10;i++) su+=sum[i]>0?sum[i]:-sum[i]; if(mx<su) { mx=su; for(int i=1;i<=n;i++) if(s>>i-1&1) as[id][i]=1; else as[id][i]=2; } } return mx; } int endro[N],k,ans[N]; int main() { freopen("train8.in","r",stdin); freopen("train8.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",op); if(op[0]=='s') { scanf("%d%d",&x1,&x2); x2=min(x2,n+1); yuu[i]=koito_yuu(1,x1,x2,0); } else if(op[0]=='v') { scanf("%d%s%s%d",&x0,t0,t1,&x1); if(t1[0]=='c') { yuu[i]=koito_yuu(2,x0,t0[0]=='+'?x1:-x1,0); } else { yuu[i]=koito_yuu(3,x0,0,x1); if(x0==12) endro[++k]=i; } } else { scanf("%s%d%s%d%d%d",t0,&x0,t1,&x1,&x2,&x3); x2=min(x2,n+1),x3=min(x3,n+1); if(t0[0]=='c'&&t1[0]=='c') yuu[i]=koito_yuu(4,x2,0,0); else if(t0[0]=='v'&&t1[0]=='c') yuu[i]=koito_yuu(5,x1,x2,x3); } } memset(dp,-0x3f,sizeof dp); dp[1][0]=0; for(int i=1;i<=k;i++) { int tp; for(int j=endro[i-1]+1;;j++) if(yuu[j].op==1&&yuu[j].b-yuu[j].a==11) { tp=j-1; break; } ll ad=solve(i,tp+1,endro[i]); for(int l=endro[i-1]+1;l<=tp;l++) for(int j=0;j<=1000;j++) { if(yuu[l].op==1) { int a=yuu[l].a,b=yuu[l].b; if(dp[a][j]<dp[l][j]) { dp[a][j]=dp[l][j]; pre[a][j]=node(l,j,1); } if(dp[b][j]<dp[l][j]) { dp[b][j]=dp[l][j]; pre[b][j]=node(l,j,2); } } else if(yuu[l].op==2) { int b=yuu[l].b; int t=max(0,j+b); if(dp[l+1][t]<dp[l][j]) { dp[l+1][t]=dp[l][j]; pre[l+1][t]=node(l,j,0); } } else if(yuu[l].op==4) { int a=yuu[l].a; if(dp[a][j]<dp[l][j]) { dp[a][j]=dp[l][j]; pre[a][j]=node(l,j,0); } } else { int a=yuu[l].a,b=yuu[l].b,c=yuu[l].c; if(j<a) { if(dp[b][j]<dp[l][j]) { dp[b][j]=dp[l][j]; pre[b][j]=node(l,j,0); } } else { if(dp[c][j]<dp[l][j]) { dp[c][j]=dp[l][j]; pre[c][j]=node(l,j,0); } } } } int s=tp+1,t=endro[i]+1; Id[s]=i; for(int j=0;j<=1000;j++) if(dp[t][j]<dp[s][j]+ad) { dp[t][j]=dp[s][j]+ad; pre[t][j]=node(s,j,3); } } //9.22925584 //10.20218188 int tj=0; for(int i=1;i<=1000;i++) if(dp[n+1][tj]<dp[n+1][i]) tj=i; int ti=n+1,cnt=0; while(ti) { int tx=ti,ty=tj; ti=pre[tx][ty].i,tj=pre[tx][ty].j; if(pre[tx][ty].cho) { if(pre[tx][ty].cho==3) { int id=Id[ti]; for(int i=ct[id];i;i--) ans[++cnt]=as[id][i]; } else ans[++cnt]=pre[tx][ty].cho; } } for(int i=cnt;i;i--) printf("%d\n",ans[i]); return 0; } ```