星空Password

cf链接

洛谷链接

这个题……首先要记清楚亮着的灯是1,不亮的是0

然后一改改一排嘛所以肯定要差分的

因为是01串所以每个位置上只能是0或者1(废话嘛

如果构造一个差分数列分别等于相邻两位是否一样

那么一个数前面每一位的“一不一样”就可以表示这一位和第一位一不一样

只要知道第一位是0还是1

也就间接地知道了这一位是多少

这个一不一样可以通过异或操作来实现

前面每一位一不一样可以求个异或前缀和

两次不一样就一样了嘛

为了方便记录

我们令b[i]=a[i]^a[i+1]

a是输入的

然后增加一位b[0]

假装在最前面的小灯泡前面还有一个小灯泡

作为上面说的已经知道了的第一位

感觉上这一位是0还是1都无所谓

实际上如果是0的话很难处理

我太菜了定为0写不出来

姑且定为1吧

那么终止状态也就是差分数组全是0

然后把翻转操作对应到差分数组上

如果说翻转i~j吧(改变j-i+1个灯泡)

那么在差分数组上实际改变的值是i-1和j(间隔j-i+1)

它们都异或了1

为了达到全0串我们不会选择i-1和j都是0的异或

只会选择全为1或者一个为1一个为0的

可以发现前一种会消除两个1而后一种1只是挪了个位置

这里的思路其实有点像经典神题独木桥

一次消除两个的话……万一是奇数个1怎么办?

这时候再次为了方便起见

在所有小灯泡后面也添一个亮着的小灯泡

差分数组的范围为从0~n

其中第n项等于最后一个真实的小灯泡异或上人为添加的小灯泡

因为最前最后两个人为添加的小灯泡都是亮着的

所以差分数组里的1肯定是偶数个

继续

那么这样的消除和挪位置不是任意长度都可以的

只有两个点间隔距离正好等于操作长度时才可以(见上文括号

可以通过bfs处理出每个1可以挪到哪里,需要多少步

另外消除两个1当然可以看成把1挪到合适距离消除

然而更好的做法是看成一个1挪到另一个1的位置上(消消乐

整理一下,现在的题目已经转化成

一个01串,其中有偶数个1且部分1可以消耗一定的步数两两消除

因为1的个数很小,可以直接状压dp

压成一个数,在这个数的二进制表示上从左到右数第i位

表示初始位置在第i个的1有没有被消除

#include<bits/stdc++.h>
using namespace std;
struct node{
	int to,nxt;
}road[3000000];
int cnt=0,head[70000],dis[30][70000],op[200],n,m,k;
int read()
{
	int x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
void add(int u,int v)
{
	road[++cnt].to=v;
	road[cnt].nxt=head[u];
	head[u]=cnt;
}
queue<int>q;
void bfs(int st,int stn)
{
	q.push(st);
	dis[stn][st]=0;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=1;i<=m;i++)
		{
			int lst=u-op[i],nxt=u+op[i];
			if(lst>=0&&dis[stn][u]+1<dis[stn][lst]) 
			{
				q.push(lst);
				dis[stn][lst]=dis[stn][u]+1;
			}
			if(nxt<=n&&dis[stn][u]+1<dis[stn][nxt]) 
			{
				q.push(nxt);
				dis[stn][nxt]=dis[stn][u]+1;
			}
		}
	}
}
int dp[4000000],pos[100],x[100],a[70000],b[70000],pcnt=0;
int solve()
{
	int st=(1<<pcnt)-1;
	dp[st]=0;
	
	for(int i=st-1;i>=0;i--)
	      for(int j=0;j<pcnt;j++)
		      if(!((1<<j)&i)) //注意这个条件
			      for(int jj=0;jj<j;jj++)
				      if(!((1<<jj)&i)) dp[i]=min(dp[i],dp[i|(1<<j)|(1<<jj)]+dis[j+1][pos[jj+1]]);
	
	return dp[0];
}
int main()
{
	n=read(),k=read(),m=read();
	for(int i=0;i<=n+1;i++) a[i]=1;
	for(int i=1;i<=k;i++) 
	{
		x[i]=read();
		a[x[i]]=0;
	}
	for(int i=0;i<=n;i++) 
	{
		b[i]=a[i]^a[i+1];
		if(b[i]) pos[++pcnt]=i;
	}
	for(int i=1;i<=m;i++) op[i]=read();
	for(int i=1;i<=n;i++)
	for(int j=0;j<=m;j++)
	{
		if(i+op[j]+1<=n) 
		{
			add(i,i+op[j]);
			add(i+op[j],i);
		}
	}
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=pcnt;i++) 
	bfs(pos[i],i);//这个东西的复杂度是O(nm)的
	memset(dp,0x3f,sizeof(dp));
	int ans=solve();
	if(ans>=0x3f3f3f3f)cout<<"-1";
	else cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/qwq-/p/14056604.html