【题解】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号