AGC061E Increment or XOR

本来想写一个有关思维过程的题解,写出来却成了这样一个怪胎,大家当个乐子看。


最近回顾了一下 Picks loves segment tree IX,然后终于会做这题了,其实两题在思维上有很大的相似之处。

核心思想是划分阶段的问题。

分析一下 +1 的操作,应该怎么考虑:

看作 bitxor 一段 1:这样需要记录末尾 1 的连续个数(最低的一个 0 在哪里),不太好做。

转换一下思路,+1 会把最后一段 10 清空,接下来的过程从一段 0 开始。状态里记录末尾 0 的个数。(这跟上面链接那题差不多)

状态记录末尾 0 的个数有什么好处?因为这是适应 +1 操作的子结构,某次 +1 后会把 [0,i] 清空,形成了一个子问题(从 0 到某个状态),初始状态就只要考虑 S/0 两种。

并且,在 +1 操作冲破第 i 位前,第 i 位都不受影响,这意味着在状态中额外记录 每个 Yi 被 XOR 了奇数还是偶数次,第 i 位就确定了。

这体现了 +1 操作的单调性:在冲到第 i 位前第 i 位都不受影响,冲到第 i 位后起点统一变成 [0,i] 全是 0 的状态。

根据单调性,可以设计从低位 dp 转移到高位的状态。下面分析一下变化的阶段。

对于最下面的若干位,整个过程是从 S0T 的。

初始状态有两种,0i1 位从 S 开始和从 0 开始。

结束状态有两种,0i1 位和 T 相同且不向 i 进位;0i1 位清成全 0 并往上进一位。

f(i,0/1,0/1,msk) 表示只考虑最低的 0i1 位(即 +1不能影响更上面的位),起点的两个情况(0/1 表示 S/0 开始),终点的两个情况(0/1 表示 T 结束并不进位 / 0 结束并进位),在这个变化过程中 msk 集合的 Yi 被 XOR 了奇数次。

f(i) 转移到 f(i+1)

转移 1:如果(进位/不进位了之后)第 i 位是匹配的,可直接转移到 f(i+1,j0,j1,msk)

转移 2:考虑进行了新的进位操作的转移。

  • 先调用 f(i,j0,1,msk) 操作,条件是从 j0 开始,这个操作完后第 i 位是 1(即进位只进到第 i 位,不能影响上面的位)。
  • 然后调用若干次 f(i,1,1,msk) 进位操作,条件是这个操作完后第 i 位是 1(即进位只进到第 i 位,不能影响上面的位)。
  • 最后调用一次 f(i,1,j1,msk) 操作,条件是进到第 i 位时符合 j1 的要求。

转移写出来就是 f(i,j0,1,msk0)+f(i,1,1,msk1)+f(i,1,1,msk2)+...+f(i,1,j1,mskl)f(i+1,j0,j1, msk)

容易发现这是一个最短路的形式,可以用 dijkstra 的方式,每次从 dp 数组中取出最小的未用过的一个,转移到其他所有的,一次 dp 复杂度就是 22n

初始状态不需要任何一位匹配,就是 f(0,j,0,msk)=xmskCx,f(0,j,1,msk)=xmskCx+A

最终答案就是 min(f40,0,0,msk),于是总复杂度 O(22nlogV)

Submission

#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;
}
posted @   Rainbow_qwq  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示