AGC061E Increment or XOR
本来想写一个有关思维过程的题解,写出来却成了这样一个怪胎,大家当个乐子看。
最近回顾了一下 Picks loves segment tree IX,然后终于会做这题了,其实两题在思维上有很大的相似之处。
核心思想是划分阶段的问题。
分析一下 的操作,应该怎么考虑:
看作 bitxor 一段 :这样需要记录末尾 的连续个数(最低的一个 在哪里),不太好做。
转换一下思路, 会把最后一段 用 清空,接下来的过程从一段 开始。状态里记录末尾 的个数。(这跟上面链接那题差不多)
状态记录末尾 的个数有什么好处?因为这是适应 操作的子结构,某次 后会把 清空,形成了一个子问题(从 到某个状态),初始状态就只要考虑 两种。
并且,在 操作冲破第 位前,第 位都不受影响,这意味着在状态中额外记录 每个 被 XOR 了奇数还是偶数次,第 位就确定了。
这体现了 操作的单调性:在冲到第 位前第 位都不受影响,冲到第 位后起点统一变成 全是 的状态。
根据单调性,可以设计从低位 dp 转移到高位的状态。下面分析一下变化的阶段。
对于最下面的若干位,整个过程是从 的。
初始状态有两种, 位从 开始和从 开始。
结束状态有两种, 位和 相同且不向 进位; 位清成全 并往上进一位。
设 表示只考虑最低的 位(即 时不能影响更上面的位),起点的两个情况( 表示 开始),终点的两个情况( 表示 结束并不进位 / 结束并进位),在这个变化过程中 集合的 被 XOR 了奇数次。
从 转移到 :
转移 1:如果(进位/不进位了之后)第 位是匹配的,可直接转移到 。
转移 2:考虑进行了新的进位操作的转移。
- 先调用 操作,条件是从 开始,这个操作完后第 位是 (即进位只进到第 位,不能影响上面的位)。
- 然后调用若干次 进位操作,条件是这个操作完后第 位是 (即进位只进到第 位,不能影响上面的位)。
- 最后调用一次 操作,条件是进到第 位时符合 的要求。
转移写出来就是 。
容易发现这是一个最短路的形式,可以用 dijkstra 的方式,每次从 dp 数组中取出最小的未用过的一个,转移到其他所有的,一次 dp 复杂度就是 。
初始状态不需要任何一位匹配,就是 。
最终答案就是 ,于是总复杂度 。
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
#define int long long
using namespace std;
#define maxn 800005
#define inf 0x3f3f3f3f3f3f3f3f
int n,S,T,A,mxs;
int x[66],c[66],sx[666],sc[666];
int f[42][2][2][260],g[260];
bool vis[260];
signed main()
{
n=read(),S=read(),T=read(),A=read(),mxs=1<<n;
For(i,0,n-1)x[i]=read(),c[i]=read();
For(s,0,mxs-1)
For(i,0,n-1)
if(s>>i&1)sx[s]^=x[i],sc[s]+=c[i];
memset(f,63,sizeof f);
For(s,0,mxs-1)
For(i,0,1) For(j,0,1) f[0][i][j][s]=sc[s]+A*j;
For(i,0,39){
// from a to b
For(j0,0,1)For(j1,0,1){
int a=j0?0:(S>>i&1);
int b=j1?1:(T>>i&1);
For(s,0,mxs-1)
if((sx[s]>>i&1)==(a^b))
f[i+1][j0][j1][s]=f[i][j0][j1][s];
}
For(j0,0,1){
int a=j0?0:(S>>i&1);
memset(g,63,sizeof g);
memset(vis,0,sizeof vis);
For(s,0,mxs-1)
if((sx[s]>>i&1)==a) g[s]=f[i][j0][1][s];
while(1){
int mn=inf,u=-1;
For(s,0,mxs-1)
if(!vis[s] && g[s]<mn)mn=g[s],u=s;
if(u==-1)break;
vis[u]=1;
For(s,0,mxs-1)
if((sx[s]>>i&1)==1)
g[s^u]=min(g[s^u],g[u]+f[i][1][1][s]);
For(j1,0,1){
int b=j1?1:(T>>i&1);
For(s,0,mxs-1)
if((sx[s]>>i&1)==(b^1))
f[i+1][j0][j1][u^s]=min(g[u]+f[i][1][j1][s],f[i+1][j0][j1][u^s]);
}
}
}
}
int res=inf;
For(i,0,mxs-1)res=min(res,f[40][0][0][i]);
if(res==inf)puts("-1");
else cout<<res;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通