P3943 星空 题解
题目链接
分析
题目中的区间反转,相当于区间异或 \(1\)
对于区间操作,我们可以想到差分
对本题来说,我们可以做一次异或差分
也就是 \(c[i]=a[i]\) 异或 \(a[i-1]\)
这样以后,对于 \([i,i+w]\) 的操作可以转化为对差分数组 \(c[i]\) 与 \(c[i+w+1]\) 取反
每次操作,我们一定会贪心的让其中一个点为 \(1\) ,只有当另一个点也为 \(1\) 时, \(1\) 的个数才会减少
所以可以用 \(bfs\) 预处理出消除任意两个 \(1\) 的最少花费
考虑到 差分后 \(1\) 的个数最多只有 \(16\) 个 (\(2*8\)) 可以想到状压 \(DP\)
然后就做完了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=500004,INF=0x3f3f3f3f;
int n,k,m,ma=0;
int ta[N];
int a[40],tot=-1;//记录为0的灯的位置
int mov[200];
int f[1<<22];
int cost[200][200];
queue<int>q;
int dis[N];
bool vis[N];
void bfs(int x){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
q.push(a[x]);vis[a[x]]=1;dis[a[x]]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=1;i<=m;i++){
if(u+mov[i]<=n&&!vis[u+mov[i]]){
dis[u+mov[i]]=dis[u]+1;
vis[u+mov[i]]=1;
q.push(u+mov[i]);
}
if(u-mov[i]>=1&&!vis[u-mov[i]]){
dis[u-mov[i]]=dis[u]+1;
vis[u-mov[i]]=1;
q.push(u-mov[i]);
}
}
}
for(int i=0;i<=tot;i++) cost[x][i]=dis[a[i]];
}
int main(){
scanf("%d %d %d", &n, &k, &m);n++;
for(int i=1;i<=k;i++){int x;scanf("%d", &x);ta[x]=1;}
for(int i=1;i<=m;i++){scanf("%d", &mov[i]);ma=max(ma,mov[i]);}
for(int i=1;i<=n;i++) if(ta[i]!=ta[i-1]) a[++tot]=i;//差分
for(int i=0;i<=tot;i++) bfs(i);
int lim=(1<<(tot+1))-1;//状压DP
for(int i=0;i<lim;i++) f[i]=INF;
for(int i=lim-1;i>=0;i--){
for(int p1=0;p1<=tot;p1++){
if((i>>p1)&1) continue;
for(int p2=p1+1;p2<=tot;p2++){
if((i>>p2)&1) continue;
f[i]=min(f[i],f[i^(1<<p1)^(1<<p2)]+cost[p1][p2]);
}
}
}
// if(f[0]==INF) cout<<-1<<endl;
else printf("%d\n", f[0]);
return 0;
}