【题解】CF79D Password
考场上背包骗了 45 pts (雾 。
考点:状压 + 差分 + 最短路
说实话第一步转移我没太看懂。(但是不影响做法
可以把题目要求变换的位置看成 1 。
问题转化为若干次操作使得原序列全为 0 。
求出差分数组,问题转化为对于新的差分序列,每次将两个位置的状态取反。
如果当前为 1 的点有奇数个的话直接输出无解。
否则容易想到两两匹配。这里可以 o(20nl) 预处理最短路。
最后观察到差分后为 1 的点的个数 <=20 ,直接状压即可。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int Maxn=1e5+5;
const int Maxm=1<<20;
int n,k,l,a[Maxn],b[Maxn],c[Maxn],g[20][20],dis[Maxn],d[Maxn],rk[Maxn],dp[Maxm],m;
vector<int> vec[Maxn];
queue<int> q;
signed main() {
	freopen("perish.in","r",stdin);
	freopen("perish.out","w",stdout);
	memset(dp,0x3f,sizeof dp);
	memset(g,0x3f,sizeof g);
	memset(rk,-1,sizeof rk);
	dp[0]=0;
	scanf("%d%d%d",&n,&k,&l);
	for(int i=1;i<=k;i++) {
		int x;
		scanf("%d",&x);
		a[x]=1;
	} 
	for(int i=1;i<=n+1;i++) {
		c[i]=a[i]^a[i-1];
		if(c[i]) rk[i]=m,d[m++]=i;
	}
	for(int i=1;i<=l;i++) {
		scanf("%d",&b[i]);
		for(int j=1;j<=n-b[i]+1;j++) {
			vec[j].push_back(j+b[i]);
			vec[j+b[i]].push_back(j);
		}
	}
	for(int i=0;i<=m;i++) {
		for(int j=1;j<=n+1;j++) dis[j]=inf;
		q.push(d[i]);
		dis[d[i]]=0;
		while(q.size()) {
			int x=q.front(); q.pop();
			for(auto y:vec[x]) {
				if(dis[y]>dis[x]+1) {
					dis[y]=dis[x]+1;
					q.push(y);
					if(~rk[y]) g[i][rk[y]]=dis[y];
				}
			}
		}
	}
	for(int i=1;i<1<<m;i++) {
		int c[20],cnt=0;
		for(int j=0;j<m;j++) {
			if(i>>j&1) {
				c[cnt++]=j;
			}
		}
		if(cnt&1) continue;
		for(int j=1;j<cnt;j++) {
			dp[i]=min(dp[i],dp[i-(1<<c[0])-(1<<c[j])]+g[c[0]][c[j]]);
		}
	}
	if(dp[(1<<m)-1]==inf) {
		printf("-1");
	}
	else {
		printf("%d",dp[(1<<m)-1]);
	}
	
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号