Codeforces Round #956 (Div. 2) and ByteRace 2024

A.思维:https://codeforces.com/contest/1983/problem/A

AC代码:

#include<bits/stdc++.h>
using namespace std;
int t;
int n;
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) cout<<i<<" ";
        cout<<endl;
    }
}

B.思维:https://codeforces.com/contest/1983/problem/B

比较考验观察能力:

1.每一次操作都不改变每一行每一列在模3意义下的总和。

因此,要保证可以转化过去,就得保证每一行列在模3意义下的总和相等。

2.满足了条件1就一定可以转化过去,我们不妨从上到下,从左到右考虑每一个点,假如他和B一样就跳过,否则一定可以用2/1补上。这样我们一定可以使(n-1)*(m-1)的矩阵和B一样,加上条件1,最后一行列也相等,证毕!

AC代码:

#include<bits/stdc++.h>
using namespace std;
int a[550][550],b[550][550];
int t,n,m;
int main(){
	cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				char c;
				cin>>c;
				a[i][j]=(int)(c-'0');
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				char c;
				cin>>c;
				b[i][j]=(int)(c-'0');
			}
		}
		int f=1;
		for(int i=1;i<=n;i++){
			int sum1=0,sum2=0;
			for(int j=1;j<=m;j++){
				sum1+=a[i][j];
				sum2+=b[i][j];
			}
			if(sum1%3!=sum2%3) f=0;
		}
		for(int j=1;j<=m;j++){
			int sum1=0,sum2=0;
			for(int i=1;i<=n;i++){
				sum1+=a[i][j];
				sum2+=b[i][j];
			}
			if(sum1%3!=sum2%3) f=0;
		}
		if(f) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
}

C.双指针:https://codeforces.com/contest/1983/problem/C

枚举中间一个分段即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n;
ll a[200010],b[200010],c[200010],tot;
ll suma[200010],sumb[200010],sumc[200010];
struct node{
	int l,r;
}res[4];
int bian(ll* sum1,ll* sum2,ll* sum3){
	int j=1;
	for(int i=1;i<=n-1;i++){
		while(sum2[j]-sum2[i-1]<tot&&j<n-1) j++;
		//看看两边是否合法
		if(sum3[n]-sum3[j]>=tot&&sum2[j]-sum2[i-1]>=tot&&sum1[i-1]-sum1[0]>=tot){
			res[1]={1,i-1},res[2]={i,j},res[3]={j+1,n};
			return 1;
		} 
	}
	return -1;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) cin>>b[i];
		for(int i=1;i<=n;i++) cin>>c[i];
		for(int i=1;i<=n;i++){
			suma[i]=suma[i-1]+a[i];
			sumb[i]=sumb[i-1]+b[i];
			sumc[i]=sumc[i-1]+c[i];
		}
		tot=suma[n]%3==0?suma[n]/3:suma[n]/3+1;
		if(bian(sumb,suma,sumc)==1){
			cout<<res[2].l<<" "<<res[2].r<<" "<<res[1].l<<" "<<res[1].r<<" "<<res[3].l<<" "<<res[3].r<<endl;
		}
		else if(bian(sumc,suma,sumb)==1) cout<<res[2].l<<" "<<res[2].r<<" "<<res[3].l<<" "<<res[3].r<<" "<<res[1].l<<" "<<res[1].r<<endl;
		else if(bian(suma,sumb,sumc)==1) cout<<res[1].l<<" "<<res[1].r<<" "<<res[2].l<<" "<<res[2].r<<" "<<res[3].l<<" "<<res[3].r<<endl;
		else if(bian(sumc,sumb,suma)==1) cout<<res[3].l<<" "<<res[3].r<<" "<<res[2].l<<" "<<res[2].r<<" "<<res[1].l<<" "<<res[1].r<<endl;
		else if(bian(suma,sumc,sumb)==1) cout<<res[1].l<<" "<<res[1].r<<" "<<res[3].l<<" "<<res[3].r<<" "<<res[2].l<<" "<<res[2].r<<endl;
		else if(bian(sumb,sumc,suma)==1) cout<<res[3].l<<" "<<res[3].r<<" "<<res[1].l<<" "<<res[1].r<<" "<<res[2].l<<" "<<res[2].r<<endl;
		else{
			cout<<-1<<endl;
		}
	}
}

D.思维+逆序对:​​​​​​https://codeforces.com/contest/1983/problem/D

首先,我们可以观察到:

1.r-l\geqslant 1的交换等价于若干个r-l=1的交换,于是条件等于两者相邻交换,交换次数相等,问是否可以变一样。

2.对于两个序列,相邻交换相同次数不改变逆序对的相对奇偶,也就是说一开始逆序对奇偶不同的一定不可以变成相等序列。

3.反之奇偶相同一定可以,因为他们一定差2的倍数,而我们可以通过1与2交换,再2与1交换抵消若干个2.

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
ll t,n;
ll a[100010],b[100010];
ll a1[100010],b1[100010];
ll cnt;
ll a2[100010];
ll e=2,ee=1;
void merge(ll i,ll mid,ll j,ll* a){
    ll x1=i,x2=mid+1,f=i;
    while(x1<=mid&&x2<=j){
        if(a[x1]<=a[x2]){
            a2[f++]=a[x1++];
        }
        else{
            a2[f++]=a[x2++];
            cnt+=mid-x1+1;
        }
    }
    while(x1<=mid){
        a2[f++]=a[x1++];
    }
    while(x2<=j){
        a2[f++]=a[x2++];
    }
    for(int h=i;h<=j;h++){
        a[h]=a2[h];
    }
}
void mergesort(int l,int r,ll* a){
	if(l<r){
		int mid=(l+r)/e;
		mergesort(l,mid,a);
		mergesort(mid+ee,r,a);
		merge(l,mid,r,a);
	}
}
ll qiu(ll* a){
	ll k=ee;
	mergesort(k,n,a);
	return cnt;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) a1[i]=a[i];
		for(int i=1;i<=n;i++) cin>>b[i];
		for(int i=1;i<=n;i++) b1[i]=b[i];
		sort(a1+1,a1+n+1);
		sort(b1+1,b1+n+1);
		int f=0;
		for(int i=1;i<=n;i++){
			if(a1[i]!=b1[i]){
				f=1;
				break;
			}
		}
		if(f){
			 cout<<"NO"<<endl;
			 continue;
		}
		cnt=0;
		ll k1=qiu(a);
		cnt=0;
		ll k2=qiu(b);
		//cout<<k1<<" "<<k2<<" ";
		if(k1%2==k2%2) cout<<"YES"<<endl;
		else cout<<"NO"<<endl; 
	}
}

E.数学期望:https://codeforces.com/contest/1983/problem/E

我们假设0为特殊球,1为普通球,假设存在一个排列:00101100.

那么Alice取的就是001和1.

接下来我们考虑每一个1对答案Alice的贡献:

因为有n-k个1,那么Alice取的都是奇数位:即(n-k+1)/2个末尾1段。

所以贡献:(S1/(n-k))*((n-k+1)/2)

考虑特殊球:

我们有n-k个1,也就是有n-k+1个空隙可以放0,因此每一个空隙相当于放了k/(n-k+1)个0,而Alice取得的是奇数段,因此贡献为:

((n-k+2)/2)*k/(n-k+1)*s2/k

也就是:((n-k+2)/2)*s2/(n-k+1)

加起来既可以,对手就是总的减去Alice即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
int n,k;
const int maxn = 5e5;
int v[maxn],sumA,sumB;
int chA,chB;
int qpow(int a,int b){
    int res=1;
    while(b){
    	if(b&1) res=(res*a)%mod;
    	b>>=1;
    	a=a*a%mod;
	}
	return res;
}
signed main(){
    int t;
    cin>>t;
    while(t--){
        sumA=sumB=chA=chB=0;
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>v[i];
            if(i<=k) sumA=(sumA+v[i])%mod;
            else sumB=(sumB+v[i])%mod;
        }
        chA=ceil((n-k+1)*1.0/2);
        chB=ceil((n-k+1)/2);
        int ans=(int)ceil((n-k)*1.0/2)*qpow(n-k,mod-2)%mod*sumB%mod+chA*qpow(chA+chB,mod-2)%mod*sumA%mod;
        ans%=mod;
        cout<<ans<<' '<<(sumA+sumB+mod-ans)%mod<<'\n';
    }
}

F.二分+01Trie:https://codeforces.com/contest/1983/problem/F

联系上次ABC上的kth不难想到先二分出一个mid,现在的问题就是有多少子对的值是小于等于它的。

我们不妨先固定r,它的贡献就是离他最近的又满足a[l]异或上a[r]小于等于mid的l的下标。

联系到二进制异或的运算可以想到01Trie,对于每一个r,我们把它和mid放目前建立好的01Trie比较,假如在一个分支上mid是1,那么我们往异或r为0的路径走就一定可以,我们对pos进行更新。然后继续往1的方向走保证考虑所有情况。反之mid那位为0,那么我们就只可以往0的方向走。最后走到头因为等于也算再更新。

实在找不到我们就用上一次可以的pos。

此时还有最后一个问题,在对pos更新时如何快速知道与r异或的l的最大下标?

我们再开一个数组在add操作中把最新的值的走的路径上的最大下标进行更新维护即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
int t,n;
ll k;
int a[500010];
int tr[5000100][2];
int tot=1; 
int ck[5000100];
int find(int x,int mid){
	int now=1;
	int pos=0;
	for(int i=29;i>=0;i--){
		int k1=((x>>i)&1),k2=((mid>>i)&1);
		if(k2==0){
			now=tr[now][k1];
		}
		else{
			pos=max(pos,ck[tr[now][k1]]);
			now=tr[now][1-k1];
		}
	}
	pos=max(pos,ck[now]);
	return pos;
}
void add(int id,int x){
	int now=1;
	for(int i=29;i>=0;i--){
		if((x>>i)&1){
			if(tr[now][1]==0) tr[now][1]=++tot;
			now=tr[now][1];
			
		}
		else{
			if(tr[now][0]==0) tr[now][0]=++tot;
			now=tr[now][0];
		}
		ck[now]=max(id,ck[now]);
	}
}
ll check(int mid){
	ll ans=0;//小于等于mid的数量 
	int pos=0;//右边界 
	for(int i=1;i<=n;i++){
		pos=max(pos,find(a[i],mid));
		ans=ans+pos;
		add(i,a[i]);
	}
	//清空
	for(int i=0;i<=tot;i++) ck[i]=0;
	for(int i=0;i<=tot;i++){
		tr[i][0]=tr[i][1]=0;
	} 
	tot=1;
	return ans;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k;
		for(int i=1;i<=n;i++) cin>>a[i];
		int l=0,r=(1<<30)-1;
		while(l<r){
			int mid=(l+r)/2;
			if(check(mid)>=k)//check(mid):小于等于mid的数量
			{
				r=mid;
			} 
			else l=mid+1;
		}
		cout<<l<<endl;
	}
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值