TopCoder 15903 EllysNim

TopCoder 15903 EllysNim(https://vjudge.net/problem/TopCoder-15903)

\(n\)看似有点东西,实际上就只是一个贪心。。。

\(i\)表示第\(i\)位,且\(i\)\(0\)开始计数

那么我们肯定是让\(i\)从高位到低位枚举,若当前位的异或值为\(1\),想办法让它变成\(0\)且不会改变更高位的异或值

首先,若我们想改变第\(i\)位的异或值,那么最优的方法肯定是将一个第\(i\)位为\(0\)的数的后\(i+1\)位加成\(2^i\),选多个肯定不如选一个优,设这个数的后\(i+1\)位为\(a\),那么代价就是\(2^i-a\),这样也不会对更高的位造成影响

但我们的选择多半不只有一个\(a\),假设当前有两个数,它们都是合法的且对应的后\(i+1\)位分别为\(a\)\(b\)\(a<b\)),如果我们把\(a\)变成\(2^i\)花费\(2^i-a\)的代价不如将\(b\)变成\(2^i\)花费\(2^i-b\)的代价,至少仅在第\(i\)位看起来是正确的,如果在后面的操作中,我们发现其实第\(i\)位选\(a\)优于\(b\),我们也可以反悔,因为我们可以将\(a\)变成\(b\)花费\(b-a\)的代价,这样总的代价就还是\(2^i-a\),也就是相当于我们将第\(i\)位选的数字从\(b\)变成了\(a\),而且现在还有了一个可以自由支配的\(b\),那么就和我们一开始舍弃的把\(a\)变成\(2^i\)\(b\)留下的方案一样了,这样就是一个反悔贪心

然后具体的,当从\(a\)变成\(b\)的时候,就是在第\(j\)位(\(j<i\)\(2^j\geq b\),因为\(2^j\geq b\)所以此时只看后\(j+1\)位的话\(a\)\(b\)都不变)选到\(a\)时,即此时剩下的所有数中,\(a\)是合法且后\(j+1\)位最大的,此时\(a\)变成\(2^j\),就相当于\(a\rightarrow b\rightarrow 2^j\),若不满足这些条件,就说明反悔了答案更劣,所以不反悔

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=105;
int n;
ll b[N],xorr,now,ans=1e18,t;
bool work(int lim){
	ll s=(1ll<<lim)-1; int pos=n;
	for(int i=0;i<n;++i) if(!(b[i]>>lim&1)&&(b[i]&s)>=(b[pos]&s)) pos=i;
	if(pos==n) return false;
	now^=b[pos],t+=(1ll<<lim)-(b[pos]&s),b[pos]=1ll<<lim;
	return true;
}
class EllysNim{
	public:
		ll getMin(vector<int> a){
			n=a.size();
			for(int i=0;i<n;++i) xorr^=a[i];
			if(!xorr) return 0;
			for(int i=30;~i;--i){//枚举有值的最高位
				if(xorr>>(i+1)) break;
				now=xorr,t=0;
				for(int j=0;j<n;++j) b[j]=a[j];
				for(int j=i;~j;--j){
					if((now>>j&1)||i==j){
						if(!work(j)){
							t=1e18;
							break;
						}
						if(i==j&&!(now>>j&1)&&!work(j)){
							t=1e18;
							break;
						}
					}
				}
				ans=min(ans,t);
			}
			return ans;
		}
};
posted @ 2023-10-13 15:13  LuoyuSitfitw  阅读(20)  评论(0编辑  收藏  举报