题解 石子游戏

传送门

所以全世界就我没看出来答案是 log 级别的
所以全世界就我两个小时才搞出来一个傻逼二分

答案是 log 级别的很好证
发现要找一个最小的集合使其异或和为某个定值
那么考虑将所有数扔到线性基里,用 log 个数一定能异或出这个数
然后就随便做了

然后二分怎么做呢?
发现有可二分性。发现 fwt 可以快速幂。没了
复杂度 \(O(n\log^2 n)\)

为什么测评不开文件啊

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 525000
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N], lim, sum;
const ll mod=1e9+7, inv2=(mod+1)>>1;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
void fwt(ll* a, int len, int op) {
	ll t1, t2;
	for (int i=1; i<len; i<<=1)
		for (int j=0,step=i<<1; j<len; j+=step)
			for (int k=j; k<j+i; ++k) {
				t1=a[k], t2=a[k+i];
				if (op==1) a[k]=(t1+t2)%mod, a[k+i]=(t1-t2)%mod;
				else a[k]=(t1+t2)*inv2%mod, a[k+i]=(t1-t2)*inv2%mod;
			}
}

namespace force{
	int ans=INF;
	void solve() {
		int lim=1<<n;
		for (int s=0; s<lim; ++s) {
			int tem=0;
			for (int i=1; i<=n; ++i) if (s&(1<<i-1)) tem^=a[i];
			if (tem==sum) ans=min(ans, __builtin_popcount(s));
		}
		cout<<n-ans<<endl;
	}
}

namespace is_fwt_be_like_this{
	ll f[N], g[N], ans[N];
	void fwt(ll* a, int len, int op) {
		ll t1, t2;
		for (int i=1; i<len; i<<=1)
			for (int j=0,step=i<<1; j<len; j+=step)
				for (int k=j; k<j+i; ++k) {
					t1=a[k], t2=a[k+i];
					if (op==1) a[k]=(t1+t2)%mod, a[k+i]=(t1-t2)%mod;
					else a[k]=(t1+t2)*inv2%mod, a[k+i]=(t1-t2)*inv2%mod;
				}
	}
	void solve() {
		n=1<<read();
		for (int i=0; i<n; ++i) f[i]=read();
		for (int i=0; i<n; ++i) g[i]=read();
		// for (int i=0; i<n; ++i)
		// 	for (int j=0; j<n; ++j)
		// 		ans[i^j]=(ans[i^j]+f[i]*g[j])%mod;
		fwt(f, n, 1); fwt(g, n, 1);
		for (int i=0; i<n; ++i) f[i]=f[i]*g[i]%mod;
		fwt(f, n, -1);
		for (int i=0; i<n; ++i) ans[i]=f[i];
		for (int i=0; i<n; ++i) printf("%lld ", (ans[i]%mod+mod)%mod); printf("\n");
	}
}

namespace task1{
	ll f[N], g[N];
	int bln, bct, ans;
	void solve() {
		for (bln=1; bln<=lim; bln<<=1,++bct);
		for (int i=1; i<=n; ++i) ++g[a[i]];
		fwt(g, bln, 1); f[0]=1;
		for (ans=0; !f[sum]; ++ans) {
			fwt(f, bln, 1);
			for (int i=0; i<bln; ++i) f[i]=f[i]*g[i]%mod;
			fwt(f, bln, -1);
		}
		printf("%d\n", n-ans);
	}
}

namespace task2{
	int bln, bct, ans;
	ll f[N], g[N], bkp[N];
	bool check(int mid) {
		for (int i=0; i<bln; ++i) f[i]=bkp[i]*qpow(g[i], mid)%mod;
		fwt(f, bln, -1);
		return f[sum];
	}
	void solve() {
		for (bln=1; bln<=lim; bln<<=1,++bct);
		for (int i=1; i<=n; ++i) ++g[a[i]];
		++g[0]; fwt(g, bln, 1);
		++bkp[0]; fwt(bkp, bln, 1);
		int l=0, r=n, mid;
		while (l<=r) {
			mid=(l+r)>>1;
			if (check(mid)) r=mid-1;
			else l=mid+1;
		}
		ans=r+1;
		printf("%d\n", n-ans);
	}
}

signed main()
{
	// freopen("nim.in", "r", stdin);
	// freopen("nim.out", "w", stdout);

	n=read();
	for (int i=1; i<=n; ++i) {
		sum^=(a[i]=read());
		lim=max(lim, a[i]);
	}
	// cout<<"sum: "<<sum<<endl;
	// force::solve();
	task2::solve();

	return 0;
}
posted @ 2022-06-03 21:18  Administrator-09  阅读(2)  评论(0编辑  收藏  举报