[計蒜客]2019 ICPC 南昌邀請賽

目錄

F.Sequence(線段樹 單點更新+區間查詢)

G.Winner(思維)

J.Prefix(字典樹)

K.A Good Game(樹狀數組+貪心)

 

F.Sequence(線段樹 單點更新+區間查詢)

題意:

定義f(l,r)=a$_{l}$ \oplus a$_{l+1}$ \oplus \cdot \cdot \cdot \oplus a$_{r}$,和

F(l,r)=f(l,l) \oplus f(l,l+1) \oplus \cdot \cdot \cdot \oplus f(l,r) \oplus f(l+1,l+1) \oplus \cdot \cdot \cdot \oplus f(l+1,r) \oplus \cdot \cdot \cdot \oplus f(r,r)

給定a$_{1}$a$_{n}$,有兩種操作:

操作1:格式爲0 x y,把ax改爲y

操作2:格式爲1 x y,求F(x,y)

分析:

不難發現,F(l,r)可由以下項異或得到:

1*(r-l+1)個a$_{l}$

2*(r-l)個a$_{l+1}$

3*(r-l-1)個a$_{l+2}$

······

(r-l+1)*1個a$_{r}$

顯然,a$_{l+1}$a$_{l+3}$a$_{l+5}$······等項異或結果爲0。

分情況討論:

情況1:r-l+1爲奇數,則F(l,r)=a$_{l}$ \oplus a$_{l+2}$ \oplus a$_{l+4}$\oplus \cdot \cdot \cdot \oplus a$_{r}$

情況2:r-l+1爲偶數,則答案爲0

思路:

建2棵線段樹,第一棵tr1只含有a數組的奇數項,偶數項全爲0,另一顆tr2只含有a數組的偶數項,奇數項全爲0。

那麼r-l+1爲奇數時:若l爲奇數,用tr1線段樹求F(l,r);若l爲偶數,用tr2線段樹求F(l,r)。

       r-l+1爲偶數時:答案爲0

單點更新時只需把更新後的值和原值異或得到的值傳入update函數進行更新即可。

代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
const int MAXN=1e5+5;
typedef long long ll;
int a[MAXN],t1[MAXN],t2[MAXN],ans;
struct node{
	int l,r,sum;
}tr1[MAXN<<2],tr2[MAXN<<2];
void build(node *tr,int *arr,int rt,int l,int r){
	tr[rt].l=l;
	tr[rt].r=r;
	if(l==r){
		tr[rt].sum=arr[l];
		return;
	}else{
		int mid=(l+r)>>1;
		build(tr,arr,rt<<1,l,mid);
		build(tr,arr,rt<<1|1,mid+1,r);
		tr[rt].sum=tr[rt<<1].sum xor tr[rt<<1|1].sum;
	}
}
void update(node *tr,int rt,int ql,int qr,int val){
	if(ql==tr[rt].l&&tr[rt].r==qr){
		tr[rt].sum=tr[rt].sum xor val;
		return;
	}
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(qr<=mid)
		update(tr,rt<<1,ql,qr,val);
	else if(mid<ql)
		update(tr,rt<<1|1,ql,qr,val);
	else{
		update(tr,rt<<1,ql,mid,val);
		update(tr,rt<<1|1,mid+1,qr,val);
	}
}
void query(node *tr,int rt,int ql,int qr){
	if(ql<=tr[rt].l&&tr[rt].r<=qr){
		ans=ans xor tr[rt].sum;
		return;
	}
	if(tr[rt].l==tr[rt].r)
		return;
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(qr<=mid)
		query(tr,rt<<1,ql,qr);
	else if(mid<ql)
		query(tr,rt<<1|1,ql,qr);
	else{
		query(tr,rt<<1,ql,mid);
		query(tr,rt<<1|1,mid+1,qr);
	}
}
int main(){
	int T,n,m,t,x,y;
	scanf("%d",&T);
	for(int i=1;i<=T;i++){
		scanf("%d%d",&n,&m);
		for(int j=1;j<=n;j++){
			scanf("%d",&a[j]);
			if(j&1){
				t1[j]=a[j];
				t2[j]=0;
			}else{
				t1[j]=0;
				t2[j]=a[j];
			}
		}
		build(tr1,t1,1,1,n);
		build(tr2,t2,1,1,n);
		printf("Case #%d:\n",i);
		for(int j=1;j<=m;j++){
			scanf("%d%d%d",&t,&x,&y);
			if(t==0){
				if(x&1){
					update(tr1,1,1,n,y xor a[x]);
				}else{
					update(tr2,1,1,n,y xor a[x]);
				}
				a[x]=y;//用完原值後更新a數組
			}else{
				ans=0;
				if((y-x+1)&1){//若r-l+1爲奇數
					if(x&1){//若l爲奇數,用線段樹tr1,否則用tr2
						query(tr1,1,x,y);
					}else{
						query(tr2,1,x,y);
					}
				}
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}

G.Winner(思維)

題意:

n個人,每個人在不同模式下有不同的能力值,共有三種模式。任選模式任選未被淘汰的人進行n-1場比賽(該模式下能力值高的人獲勝),最後一個人獲勝。問:給定第k個人,他有沒有可能贏?

思路:

假定第i個人有可能贏,那麼你只要在某一模式下能贏他,那麼你就能贏。因爲你能贏第i個人,然後他幫你贏其他所有人。

首先輸入的同時記錄該模式下能力值最大的那個人的id,這個人有可能贏。然後記錄能贏的人他們的不同模式下能力值的最小值。(即你只需要在某一模式下大於這個最小能力值,你也能贏)

每次從頭遍歷,如果找到一個還不知道能不能贏的人,他在某一模式下比能贏的所有人在該模式下能力值的最小值還大,那麼這個人也能贏,如果找不到,結束循環,剩下所有還不知道能不能贏的人都是不可能贏的。

隊友敲的代碼:

#include<cstdio>
#include<cstring>
int a[4][100010];
bool vis[100010];
int vnum[4]={0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f};
void changemin(int j){
    for(int i=1;i<=3;i++){
        if(a[i][j]<vnum[i]) vnum[i]=a[i][j];
       // printf("%d\n",vnum[i]);
    }
}
int main(){
    int n,m;
    int id[4];
    memset(vis,false,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=3;i++){
        int maxn=0;
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
            if(a[i][j]>maxn) {
                id[i]=j;
                maxn=a[i][j];
            }
        }
        vis[id[i]]=true;
    }
    //for(int i=1;i<=3;i++)printf("%d ",id[i]);
    //printf("\n");
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            //printf("%d %d %d ",i,id[j],a[i][id[j]]);
            if(vnum[i]>a[i][id[j]]) vnum[i]=a[i][id[j]];
        }
        //printf("\n");
    }
    int modi=1;
    //for(int i=1;i<=3;i++)printf("%d\n",vnum[i]);
    while(modi){
        int flag=0;
        for(int i=1;i<=3;i++){
            for(int j=1;j<=n;j++){
                if(vis[j]) continue;
                if(a[i][j]>vnum[i]){
                    changemin(j);
                    flag=1;
                    vis[j]=true;
                }
            }
        }
        if(flag==0) modi=0;
    }
    int p;
    for(int i=1;i<=m;i++) {
        scanf("%d",&p);
        if(vis[p]) printf("YES\n");
        else printf("NO\n");
    }
}

J.Prefix(字典樹)

分析:

隊友說是字典樹模板題,emmm,然而弱雞的我並不會字典樹······

隊友敲的代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
string s[maxn];
int Trie[10*maxn][26],tot=1;
ll d[26],D[maxn],n,m;
struct Node{
	ll cnt,dif;
	Node(){ cnt=0; dif=0;  };
}node[maxn*10];

void insert(int id){
	int len=s[id].size();
 	int p=0; ll dif=1;
 	for(int i=0;i<len;++i){
 	 	int k=s[id][i]-'a';
 	 	if(Trie[p][k]==0)
  			 Trie[p][k]=tot++;
  		p=Trie[p][k];
  		dif*=d[k];
  		dif%=m;
  		node[p].cnt++; node[p].dif=dif;
 	}
 	D[id]=dif;
}

ll query(int id){
	 int len=s[id].size();
	 int p=0; ll ans=0;
	 for(int i=0;i<len;++i){
	 	int k=s[id][i]-'a';
		p=Trie[p][k];
  		if(node[p].dif>D[id])
 		  ans+=node[p].cnt;
	}
 	return ans;
}

int main(){
 	cin>>n>>m;
 	for(int i=0;i<26;++i) cin>>d[i];//scanf("%lld",&d[i]);
 	for(int i=1;i<=n;++i){
 	 cin>>s[i];
 	 insert(i);
 	}
 	for(int i=1;i<=n;++i){
  		cout<<query(i);//printf("%lld",query(i));
  		if(i!=n) putchar(' ');
 		 else putchar('\n');
 	}
	 return 0;
}

K.A Good Game(樹狀數組+貪心)

題意:

給定數組a,每次的區間求和得到數組b,把b由大到小排序,分別乘以n,n-1,······,1,得到最大值。

分析:

使用樹狀數組,求和,排序,求最大值。

代碼:

#include<cstdio>
#include<string>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100010;
int bit[MAXN+1],n,m;
ll sum(int i){
	ll s=0;
	while(i>0){
		s+=bit[i];
		i-=i&-i;
	}
	return s;
}
void add(int i,int x){
	while(i<=n){
		bit[i]+=x;
		i+=i&-i;
	}
}
ll s[MAXN+1],ans;
int main(){
	int T,t,l,r;
	scanf("%d",&T);
	while(T--){
		memset(bit,0,sizeof(bit));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			scanf("%d",&t);
			add(i,t);
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&l,&r);
			s[i]=sum(r)-sum(l-1);
		}
		sort(s+1,s+m+1);
		ans=0;
		for(int i=m;i>0;i--){
			ans+=s[i]*i;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

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