CF1148F Foo Fighters(构造,贪心)

洛谷传送门


解题思路

按mask二进制位,把物品分类。
设sum[i]为mask共有i位的物品的val的和。
因为若答案的第i位为1,对mask小于i位的物品是没有影响的。
于是我们从低位向高位枚举,这样就保证了后面的决策不会影响前面已经决定了的状态。
因为只要求答案变成相反数,所以只要每一个sum[i]变成与总的sum异号即可。
同时,在更改当前位时,需要把当前位为1的每一个物品的val变成相反数并更新后面的sum。
感觉这不是一个真正意义上的贪心,其并不满足最优子结构的要求。
因为这样只能求出一组解,而不是最优解。

注意:用 1<<i 时,一定要写成 1ll<<i。
一天一个好习惯,noip不爆零。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=3e5+5;
int n,ans[65];
long long anss,sum[maxn],tot;
struct node{
	long long val,mask;
}a[maxn];
vector<int> b[maxn];
inline void update(int i,int p){
	int cnt=0;
	long long x=a[i].mask;
	if(x&(1ll<<(p-1))){
		while(x>0){
			x/=2;
			cnt++;
		}
		sum[cnt]-=2*a[i].val;
		a[i].val=-a[i].val;
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].val>>a[i].mask;
		tot+=a[i].val;
		long long t=a[i].mask,cnt=0;
		while(t>0){
			t/=2;
			cnt++;
		}
		b[cnt].push_back(i);
		sum[cnt]+=a[i].val;
	}
	for(int i=1;i<=63;i++){
		if((sum[i]!=0)&&(((sum[i]>0)^(tot>0))==0)){
			ans[i]=1;
			for(int j=1;j<=n;j++){
				update(j,i);
			}
		}
	}
	for(int i=63;i>=1;i--){
		anss=anss*2+ans[i];
	}
	cout<<anss;
    return 0;
}