Codeforces Global Round 16 题解

传送门:Dashboard - Codeforces Global Round 16 - Codeforces

老鸽子终于记得更新了2333

暑假之前:老子要更新gdcpc的题解

暑假开始:去他丫的题解


A. Median Maximization

给n,m,构造出一个非负的递增数列,使其中的元素个数为n,元素和为m,求这些数列的中位数的最大值(此处中位数的定义为2k+1的第k+1个,2k的第k个)

1 5➡5
2 5➡2 3
3 5➡0 2 3
2 1➡无解
7 17➡0 0 0 4 4 4 5
4 14➡0 3 4 4
1 1000000000➡1000000000
1000000000 1➡无解

很显然,为了让中位数尽量大,中位数前面的数据要尽可能小,即为0,后面要尽可能平均,打表易得结果

#include<bits/stdc++.h>
using namespace std;
int t,n,m,k;
int main() {
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
			if(n%2==0)
			k=n/2-1;
			else
			k=n/2;
			n=n-k;
			cout<<m/n<<"\n";	
	 } 
}


B. MIN-MEX Cut

给一个01串,可以把它分割也可以不操作,求字串的mex和

mex值是字串中没用出现过的第一个自然数

很容易得知,不操作的时候mex=2

mex(0……0)=1

mex(1……1)=0

直接将0……0找出来就行

结果和2对比,取最小

#include<bits/stdc++.h>
using namespace std;
int t,n,m,k;
char a[200000];
int main() {
	cin>>t;
	while(t--)
	{
		cin>>a;	
		int len=strlen(a);
		int sum=0;
		
		for(int i=0;i<len;i++)
		{
			if(a[i]=='0'&&(a[i+1]=='1'||i==len-1))
			{
				sum++;
			}
		}
		cout<<min(sum,2)<<"\n";
		
	 } 
}


C

两个并列的01串,竖着切几刀将其分割,求分割了之后的mex和最大值

首先把每一位都分割开来,有1/0、0/0、1/1三种情况mex值为2,1,0

第一种情况和前后结合都会使总和变小

后两种情况结合起来使得总和变大

每种情况自己结合都使得总和变小

综上

先按位分割

再适当结合

#include<bits/stdc++.h>
using namespace std;
int t,n,m,a1,b1;
char a[200000],b[200000];
int c[200000],k[200000];
int main() 
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		cin>>a>>b;	
	//	int len=strlen(a);
		int sum=0;
		
		for(int i=0;i<n;i++)
		{
			k[i]=0;
			a1=a[i]-'0';
			b1=b[i]-'0';
			if(a1+b1==1)//情况1
			{
				c[i]=2; 
			}
			if(a1+b1==2)//情况3
			{
				c[i]=0; 
			}
			if(a1+b1==0)//情况2
			{
				c[i]=1; 
			}
		}
	 
	for(int i=0;i<n;i++)
	 {
	 	if(c[i]==1)//先前后后
	 	{
	 		if(i!=0&&c[i-1]==0&&k[i-1]==0)
			{
			 	c[i]=2;
			 	k[i-1]=1;//k用于标记此位是否已经被结合
			} 
			else
			{
				if(i!=n-1&&c[i+1]==0&&k[i+1]==0)
				{
					c[i]=2;
					k[i+1]=1; 
				}
			}
		 }
	}
	 
	 for(int i=0;i<n;i++)
	 {
	 	sum+=c[i];
	 }
	 cout<<sum<<"\n";
	 } 
	 return 0;
}


D1

给m个人的视力值,可能相同可能不同,这m个人坐在1-m的座位(同一排),视力值越小的人的位置值越小,每个人按顺序进场从位置1坐到自己的位置上,求越过的人次的总和的最小值

如果某个人的视力值和所有人都不同,那么他的位置是唯一的

如果有k个人的视力值相同,将他们单独抓出来看,越早进场的人坐到越后面,就不会越过相同视力的人

因此每个人按照从小到大视力排列,相同视力的时候按照id从大到小排列,此时得到每个人的座位值

再按照id排序,模拟进场,并求已经进场的座位值比第i个人的座位值的个数,可用树状数组求

(为啥最近老是喜欢用树状数组orz)

#include <bits/stdc++.h>
 using namespace std;
 int n,m;
int g[50005]; //对应原数组和树状数组
 int lowbit(int x){
     return x&(-x);
  }
 
 void updata(int i,int k,int m){    //在i位置加上k
    while(i <= m){
        g[i] += k;
       i += lowbit(i);
    }
}

int getsum(int i){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += g[i];
        i -= lowbit(i);
    }
    return res;
 }
 struct xd{
 	int a,b,c;//a 视力 b 顺序 c 位置 
 };
 xd z[600];
 int cmp(xd y,xd x)
 {
 	if(y.a==x.a) return y.b>x.b;
 	return y.a<x.a;
 }
 int cmp2(xd y,xd x)
 {
 	return y.b<x.b;
 }
int main(){
     int t,n,m,sum,q;
    cin>>t;
    while(t--)
    {
    	memset(g, 0, sizeof g);
    	sum=0;
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)
    	{
    		cin>>z[i].a;
    		z[i].b=i;
		}
		sort(z+1,z+1+m,cmp);
		for(int i=1;i<=m;i++)
		{
			z[i].c=i;
		}
		sort(z+1,z+1+m,cmp2);
		for(int i=1;i<=m;i++)//a 视力 b 顺序 c 位置  
		{
			q=getsum(z[i].c);
			sum+=q;
			updata(z[i].c,1,m);   //输入初值的时候,也相当于更新了值
		}
		cout<<sum<<"\n";
	}
    
   return 0;
 }

D2

多了个n行,此时第一次排序按照视力从小到大,id从小到大排序

再分别在每排排序,按照视力从小到大,id从大到小排序

求出位置值

然后同上

#include <bits/stdc++.h>
 using namespace std;
 int n,m;
int g[600][600]; //对应原数组和树状数组
 int lowbit(int x){
     return x&(-x);
  }
 
 void updata(int i,int k,int m,int l){    //在i位置加上k
    while(i <= m){
        g[i][l] += k;
       i += lowbit(i);
    }
}

int getsum(int i,int l){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += g[i][l];
        i -= lowbit(i);
    }
    return res;
 }
 struct xd{
 	int a,b,c;//a 视力 b 顺序 c 位置 
 };
 xd z[360000];
 int cmp(xd y,xd x)
 {
 	if(y.a==x.a) return y.b>x.b;
 	return y.a<x.a;
 }
 int cmp2(xd y,xd x)
 {
 	return y.b<x.b;
 } 
 int cmp3(xd y,xd x)
 {
 	if(y.a==x.a) return y.b<x.b;
 	return y.a<x.a;
 }
int main(){
    long long t,n,m,sum,q,p;
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
    	memset(g, 0, sizeof g);
    	sum=0;
    	cin>>n>>m;
    	for(int i=1;i<=m*n;i++)
    	{
    		cin>>z[i].a;
    		z[i].b=i;
		}
		sort(z+1,z+1+m*n,cmp3);
		for(int i=1;i<=n;i++)
		{
			sort(z+1+(i-1)*m,z+1+i*m,cmp);
			for(int j=1;j<=m;j++)
			{
				z[(i-1)*m+j].c=(i-1)*m+j;
			}
		}
		sort(z+1,z+1+m*n,cmp2);
		for(int i=1;i<=m*n;i++)//a 视力 b 顺序 c 位置  n排m列 
		{
			if(z[i].c%m==0) //q 行 p列 
			{
				q=z[i].c/m;
				p=m;
			}
			else 
			{
				q=z[i].c/m+1;
				p=z[i].c%m;
			}
			sum+=getsum(p,q);
			updata(p,1,m,q);   //输入初值的时候,也相当于更新了值
		}
		cout<<sum<<"\n";
	}
    
   return 0;
 }

后面懒得补了下次吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值