計蒜客 ACM-ICPC 2017 Asia Xi

收錄兩題  還有大家千萬別用uva的題目來源 這oj真的垃圾 毀我青春

A1607   XOR  

題意:給一個長度爲n的序列 給一個數k  每次詢問區間L,R  在L,R這些數中選一個子集  使得子集中的元素的異或和與k進行或運算以後取得最大值

顯然 由或運算的性質我們知道 k的二進制中爲1的位會保留 爲了取得最大值 我們得儘量讓爲0的位變成1      我們可以讓序列中每個數都進行 ai&=(~k) 這樣的一個運算 那麼ai留下來的就是k中沒有的位了   然後我們用線段樹 維護a數組的區間線性基  對於詢問 L,R 我們得到該區間的線性基並且給出最大值mx  最後 k|mx 就是答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+7;
int a[N]; 
int ans[30]; 
int tree[N<<2][30];
void add(int b[],int x) {
	for(int i=27;i>=0;i--) {
		if(x&(1LL<<i)) {
			if(!b[i]) {
				b[i]=x;
				break;
			}
			else x^=b[i];
		}
	}
}
void pushup(int rt) {
	for(int i=27;i>=0;i--)
		tree[rt][i]=tree[rt<<1][i];
	for(int i=27;i>=0;i--) 
		add(tree[rt],tree[rt<<1|1][i]); 
}
void build(int rt,int l,int r) {
	if(l==r) {
		add(tree[rt],a[l]);
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	pushup(rt);
}
void query(int rt,int l,int r,int L,int R) {
	if(R<l||L>r) return;
	if(L<=l&&r<=R) {
		for(int i=27;i>=0;i--)
			add(ans,tree[rt][i]);
		return; 
	}
	int mid=(l+r)>>1;
	query(rt<<1,l,mid,L,R);
	query(rt<<1|1,mid+1,r,L,R);
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		int n,q,k;
		scanf("%d%d%d",&n,&q,&k);
		for(int i=1;i<=n;i++) {
			scanf("%d",&a[i]);
			a[i]=a[i]&(~k);
		}
		build(1,1,n);
		while(q--) {
			int l,r;
			scanf("%d%d",&l,&r);
			for(int i=27;i>=0;i--) ans[i]=0;//清空答案  
			query(1,1,n,l,r);
			int res=0;
			for(int i=27;i>=0;i--) { //獲得最大值 
				if((res^ans[i])>res) {
					res^=ans[i]; 
				}
			}
			printf("%d\n",res|k);
		}
	}
	return 0;
}

A1613   Sum of xor sum

題意:給一個長度爲n的序列  給出區間L,R  求該區間所有子區間的異或和的和

這算是個比較套路的題吧 bzoj上似乎也有類似的題   首先進行二進制拆分  對於每一位 我們做一個前綴異或  然後統計這個區間的前綴異或中0和1的個數就行  因爲是前綴異或 我們需要加一個虛點0進去 即a[0]=0;

用sum0[i][j] 表示前i+1個數(包括a[0])在第j位的前綴異或爲0的個數 sum1[i][j] 同理 

根據前綴異或的性質  

我們考慮區間L到R第t位的貢獻(從0開始)

 ans=(sum[R][t]-sum[L-2][t])*(1<<t)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
int a[N];
const ll mod = 1e9+7;
ll sum1[N][30],sum0[N][30];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(sum0,0,sizeof(sum0));
		memset(sum1,0,sizeof(sum1));
		int n,q;
		scanf("%d%d",&n,&q);
		for(int i = 1; i <= n; i++){
			scanf("%d",&a[i]);
			a[i]^=a[i-1];
		}
		for(int j = 29; j >= 0; j--){
			for(int i = 0; i <= n; i++){
				if(i){
					sum1[i][j]+=sum1[i-1][j];
					sum0[i][j]+=sum0[i-1][j];
				}
				if((a[i]>>j)&1) sum1[i][j]++;
				else sum0[i][j]++;
			}
		}
		for(int i = 1; i <= q; i++){
			int l,r;
			scanf("%d%d",&l,&r);
			ll ans = 0;
			for(int j = 29; j >= 0; j--){
				if(l==1) 
				ans+=((sum1[r][j])*(sum0[r][j])%mod*(1ll<<j))%mod;
				else 
				ans+=((sum1[r][j]-sum1[l-2][j])*(sum0[r][j]-sum0[l-2][j])%mod*(1ll<<j))%mod;
				//printf("sum1[r][j]=%lld sum1[l-1][j]=%lld sum0[r][j]=%lld sum0[l-1][j]=%lld\n",sum1[r][j],sum1[l-1][j],sum0[r][j],sum0[l-1][j]);
				ans%=mod;	
			}	
			printf("%lld\n",ans);
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章