P9017 [USACO23JAN] Lights Off G 题解

一次操作相当于把 \(a\) 异或上 \(b\),修改开关的一位相当于将这一位异或上 \(1\)

会发现一个很神奇的性质:初始开关对灯的影响和改变开关状态对灯的影响是独立的。而前者的影响是固定的,所以我们可以只考虑改变开关状态对灯的影响。假设一共需要 \(k\) 次操作能使所有灯关闭,如果我们在第 \(i\) 次操作的时候改变了开关 \(p\) 的状态,那么第 \(i+1\) 次的时候这个开关会影响到 \(p+1\)(因为要循环右移一位),第 \(i+2\) 次操作会影响到 \(p+2\)。所以如果在第 \(i\) 次操作改变一个开关的状态,那么相当于将长度为 \(m-i+1\) 的一段区间全部影响了(异或上 \(1\))。那么如果我们一共需要 \(k\) 次操作,那么我们可以分别修改长度为 \(1\)\(k\) 的区间各一次。注意这里的区间指的不一定是连续的,也有可能是一段前缀加一段后缀(因为可能循环右移)。并且如果一个点被修改(异或)两次的话相当于不修改。

\(dp_{i,j}\) 表示 \(i\) 次操作(第 \(i\) 次操作指的是将一段长度为 \(i\) 的区间异或上 \(1\))之后能否达到 \(j\) 状态。那么转移方程为 \(dp_{i,j}=dp_{i,j} | dp_{i-1,j ^ k}\),其中 \(k\) 是长度为 \(i\) 的区间。时间复杂度 \(O(2^n\times n^2)\),可以通过。

还可以优化。如果一个状态是另一个状态通过循环右移得到的,那么这两个状态的 \(dp\) 值一定相同,可以只用记录一次。时间复杂度 \(O(2^n \times n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int f[45][N],t,n,p[N];
char a[N],b[N];
int g(int x){return (x >> 1) + ((x & 1) << n - 1);}
signed main()
{
	cin >> t >> n;
	memset(p,-1,sizeof p);
	for(int i = 0;i < (1 << n);i++)
	{
		int x = i;
		while(p[x] == -1) p[x] = i,x = g(x); 
	}
	f[0][0] = 1;
	for(int i = 1,s = 0;i <= 2 * n;i++)
	{
		s ^= (1 << ((i - 1) % n));
		for(int j = 0;j < (1 << n);j++)
			f[i][p[j]] |= f[i - 1][p[j ^ s]];
	}
	while(t--)
	{
		cin >> a >> b;
		int A = 0,B = 0;
		for(int i = 0;i < n;i++)
			A |= ((a[i] - '0') << (n - i - 1)),
			B |= ((b[i] - '0') << (n - i - 1));
		if(A == 0){puts("0");continue;}
		for(int i = 1;;i++)
		{
			A ^= B,B = g(B);
			if(f[i][p[A]]){cout << i << endl;break;}
		}
	}
	return 0;
}

posted @ 2024-01-17 09:17  Creeper_l  阅读(3)  评论(0编辑  收藏  举报