这个起名方式居然还有后续?!
为什么起名不是连续的?!
T1想了半天,搞出来了,结果数组开小【其实是没注意范围】。T2概率期望直接跳,后来翻回来写发现自己整个理解错了期望的含义【何】。T3错误想到赛道修建结果来了个错误贪心。
关于T2破罐子破摔输出k居然骗了二十分这件事……
T1u:
一开始各种想偏,维护哪种值是奇数或偶数个,考虑每次操作影响哪些值变化…这些全都跑出来了。
大概过了一个世纪那么长,突然想着能不能直接优化操作的过程啊,然后对暴力进行钻研,终于开始想到差分。
然后觉着nq的复杂度过不去,开始对差分进行瞎搞。终于又过了一个世纪那么久,发现差分数组也是可以传递转移的……
两个世纪过去了,没有然后了。
#include<iostream> #include<cstdio> using namespace std; int n,q; long long ans,f[2010][2010],H[2010][2010],L[2010][2010],sum; int main() { scanf("%d%d",&n,&q); if(!q){ printf("0 "); return 0; } else if(n<=300){ for(int i=1,r,c,l,s;i<=q;i++){ scanf("%d%d%d%d",&r,&c,&l,&s); for(int j=r;j<r+l;j++){ for(int k=c;k<=j-r+c;k++){ f[j][k]+=s; } } } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ ans^=f[i][j]; } } printf("%lld ",ans); return 0; } else if(q<=2000){ for(int i=1,r,c,l,s;i<=q;i++){ scanf("%d%d%d%d",&r,&c,&l,&s); for(int j=r;j<r+l;j++){ H[j][c]+=s; H[j][j-r+c+1]-=s; } } for(int i=1;i<=n;i++){ sum=0; for(int j=1;j<=n;j++){ sum+=H[i][j]; ans^=sum; } } printf("%lld ",ans); return 0; } else{ for(int i=1,r,c,l,s;i<=q;i++){ scanf("%d%d%d%d",&r,&c,&l,&s); H[r][c]+=s; L[r][c+1]-=s; H[r+l][c]-=s; L[r+l][c+l+1]+=s; } for(int i=1;i<=n;i++){ sum=0; for(int j=1;j<=n;j++){ H[i][j]+=H[i-1][j]; L[i][j]+=L[i-1][j-1]; sum+=H[i][j]+L[i][j]; ans^=sum; } } printf("%lld ",ans); return 0; } }
T2v:
整个儿弄错了期望这东西,一言难尽。
题目中的最优策略,指的是在以某一概率选到某一个位置的时候,选择拿前面的球或者拿后面的球的其中一种决策产生的贡献,累计上这个球是不是白球的贡献,取最大值。
所以dfs,枚举选到的是哪个位置,累积贡献,除以可选到多少种位置,就是这一状态的期望。
因为搜索到的状态会有很多重复,考虑记忆化搜索。但是表示某一状态的数字可能很大,数组下标无法表述。那么对于数组表示得出来的,就用数组存。其它更大的用map映射解决。
不同长度的状态可能存在数值上相同的情况,例如010和0010。解决方法是在这一长度的再靠前一位打上一个1的标记,例如010->1010,0010->10010,这样表示不同的状态。
#include<iostream> #include<cstdio> #include<map> using namespace std; int n,k; char s[31]; long long fir; double ans,num[(1<<25)]; map<long long,double>m; double dfs(long long x,int k,int lon){ if(!k)return 0; if(lon<=25&&num[x]!=-1.0)return num[x]; else if(m.find(x)!=m.end())return m[x]; double f=0,f1=0,val1,val2; long long x1,x2; for(int i=1;i<=lon/2;i++){ val1=(x>>(lon-i-1))&1,val2=(x>>(i-1))&1; f1=0; x1=(x>>1)&(~((1<<(lon-i-1))-1))|(x&((1<<(lon-i-1))-1)); x2=(x>>1)&(~((1<<(i-1))-1))|(x&((1<<(i-1))-1)); f1=dfs(x1,k-1,lon-1)+val1; f1=max(f1,dfs(x2,k-1,lon-1)+val2); f+=f1*(i==lon-i?1.0:2.0); } f/=(double)(lon-1); if(lon<=25)num[x]=f; else m[x]=f; return f; } int main(){ scanf("%d%d",&n,&k); scanf("%s",s+1); for(int i=0;i<=(1<<25);i++)num[i]=-1.0; fir=1; for(int i=1;i<=n;i++){ fir=(fir<<1)+(s[i]=='W'); } ans=dfs(fir,k,n+1); printf("%.10lf",ans); return 0; }
T3w:
错误贪心:强行认为每个节点由子节点传上来的路径若有偶数条就自行解决是最优的,奇数条就看当前节点连出去的边目标是不是2,然后无脑传上去。
错误性显而易见……有可能强行传上去的这条会打乱父节点本来可以完整自行配对的组合,给父节点增加一条路径,然后不得不再次穿上去,又增加了长度……还不如在当前节点自行了断。
正解是树形DP。发现最后的总路径数是整棵树路径中的奇度数点数/2。对于每个点,记录它传一条边上去或者不传的时候dp[x][1],dp[x][0],包含子树的最少奇度数点数以及在此基础上的最小总路径长。
一棵一棵子树进行转移。设两个用来统计子树们的最优答案的二元组w1,w2。w1的含义是子树们传了一条边上来,w2是不传让它们两两解决。对于每棵子树y,w1=min(w2+dp[y][1],w1+dp[y][0]),w2=min(w1+dp[y][1],w2+dp[y][0])。比较大小的时候都优先比较奇度数点数c1,再比较路径长度c2。
然后根据当前节点上面这条边的状态和目标状态计算dpx。显然一条边如果已经是目标状态,那么始终不走一定是更优的。如果这条边已经是目标状态,让dp[x][1]=inf。使w1的c1++,w2不变,表示若有边传上来就多一个奇度数点作为收尾,或者没有边传上来就不变,然后dp[x][0]在w1和w2中选择更优的。如果这条边不是目标状态,就让dp[x][0]=inf。w1.c2++。w2.c1++,w2.c2++,表示这个节点作为一个新的开头使奇度数节点增加。依然让dp[x][1]在w1和w2中选择更优的。如果这条边的目标状态是2,就两种情况非inf的部分都做一遍。
#include<iostream> #include<cstdio> using namespace std; const int inf=214748364; int f[100010][2],opt[100010],goal[100010]; struct node{ int c1,c2; }dp[100010][2],tool; int n,tot,ver[200010],head[100010],edge[200010],edge1[200010],Next[200010]; void add(int x,int y,int z,int w){ ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; edge[tot]=z; edge1[tot]=w; } node work(node a,node b,node c,node d){ node e; if(a.c1+b.c1<c.c1+d.c1){ e.c1=a.c1+b.c1; e.c2=a.c2+b.c2; } else if(a.c1+b.c1==c.c1+d.c1){ if(a.c2+b.c2<c.c2+d.c2){ e.c1=a.c1+b.c1; e.c2=a.c2+b.c2; } else{ e.c1=c.c1+d.c1; e.c2=c.c2+d.c2; } } else{ e.c1=c.c1+d.c1; e.c2=c.c2+d.c2; } return e; } void dfs(int x,int fa){ node w1,w2; w1.c1=w1.c2=inf; w2.c1=w2.c2=0; for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(y==fa)continue; opt[y]=edge[i]; goal[y]=edge1[i]; dfs(y,x); node w11=w1; w1=work(dp[y][0],w1,dp[y][1],w2); w2=work(dp[y][1],w11,dp[y][0],w2); } if(goal[x]==2){ w2.c1++; w1.c2++; w2.c2++; dp[x][1]=work(w1,tool,w2,tool); w2.c1--; w1.c2--; w2.c2--; w1.c1++; dp[x][0]=work(w1,tool,w2,tool); } else{ if(opt[x]!=goal[x]){ dp[x][0].c1=inf,dp[x][0].c2=inf; w2.c1++; w1.c2++; w2.c2++; dp[x][1]=work(w1,tool,w2,tool); } else{ dp[x][1].c1=inf,dp[x][1].c2=inf; w1.c1++; dp[x][0]=work(w1,tool,w2,tool); } } } int main() { scanf("%d",&n); for(int i=1,x,y,z,w;i<n;i++){ scanf("%d%d%d%d",&x,&y,&z,&w); add(x,y,z,w),add(y,x,z,w); } dfs(1,0); printf("%d %d ",dp[1][0].c1/2,dp[1][0].c2); return 0; }
结果拖到这会儿才写完。
祝祖国母亲生日快乐。