• CodeForces 1406


    (注:本来开了个坑,原标题为「do you know wtf it is???」,正文为「懵逼了吧」,所以被 hsc 骂了……)

    这场比较简单,所以本文的重心放在一波三折的比赛(赛后?)经历上(

    本来想着用 tzcWh... 这个号控制住不要上橙,大概控制在 2070~2099 左右,然后下一场 div. 2 就能超过大号这样。

    比赛结束前 5min E 交上去 WA 了,于是弃了,看了眼 predictor,能直接上 2150,这不行啊。于是开始故意 hack 失败,网速比较慢所以 5min 只 hack 了 5 次。

    减了 250pts 之后比赛结束,又看了一眼 predictor,+130?掐指一算,涨到 2102?wtf???要上橙就给我上高一点,要么就不要上橙,ntm 给我搭个橙名线是几个意思?很后悔没有早点开始 hack,多 hack 一次就 CM 稳了。然后就跟 wjz 疯狂祖安。

    然后突然发现我的 A 好像 FST 了,上面出现了个红色的 -1?那 tgxl,rating 低一点没关系,至少不用上橙了!然后 wjz 说「这是 feature,你刷新一下」(老 system test 观众了),然后又没 FST,得分了。还我 FST!!!于是继续祖安,祖安累了去洗了个澡。

    洗完澡回来之后发现 wjz 跟我说「不会 master」,又是啥?system test 已经结束了,打开 predictor 一看 +124,2096?tgxl,这个结果我满意。于是开开心心睡觉去了。

    早上起来忐忑地打开电脑,发现居然 +127,2099?这无疑是我最满意的结果,请叫我控分带师(

    也许这就是命运吧(


    下面是题解(正文部分)

    CF 比赛页面传送门

    A - Subset Mex

    洛谷题目页面传送门 & CF 题目页面传送门

    给定一个集合 (a,|a|=n),将它分成两个集合 (A,B),要求最大化 (operatorname{mex}(A)+operatorname{mex}(B))。本题多测。

    (nin[1,100],a_iin[0,100],Tin[1,100])

    随便贪心就好了,两个集合的 (operatorname{mex}) 齐头并进,某个进不了了就停下来,两个都停下来的时候就是答案最大化的情况。代码也随便写了吧。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100;
    int n;
    int a[N+1];
    int cnt[N+1];
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	sort(a+1,a+n+1);
    	memset(cnt,0,sizeof(cnt));
    	for(int i=1;i<=n;i++)cnt[a[i]]++;
    	int fst=0;
    	for(int i=0;i<=100;i++)if(cnt[i]<2){fst=i;break;}
    	for(int i=fst;i<=100;i++)if(cnt[i]<1)return cout<<fst+i<<"
    ",void();
    	if(fst)cout<<fst+101<<"
    ";
    	else cout<<202<<"
    ";
    }
    int main(){
    	int testnum=1;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    B - Maximum Product

    洛谷题目页面传送门 & CF 题目页面传送门

    给定 (n) 个整数(可正可负可零),求其中 (5) 个数的乘积的最大值。本题多测。

    (ninleft[5,10^5 ight],sum nleq 2 imes10^5)

    这个就分个两种情况吧,有 (0) 和无 (0)

    (0) 的话就是 (0) 了。

    (0) 的话就枚举正数个数,然后排个序贪个心就切了。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pb push_back
    const int inf=0x3f3f3f3f3f3f3f3f;
    int n;
    void mian(){
    	cin>>n;
    	vector<int> po,ne;
    	int ans=-inf;
    	for(int i=1;i<=n;i++){
    		int x;
    		cin>>x;
    		if(x>0)po.pb(x);
    		if(x==0)ans=0;
    		if(x<0)ne.pb(x);
    	}
    	sort(po.begin(),po.end(),greater<int>());
    	sort(ne.begin(),ne.end());
    	for(int i=0;i<=5;i++){
    		if(i<=po.size()&&5-i<=ne.size()){
    			int res=1;
    			if(5-i&1){
    				for(int j=(int)(po.size())-1;j>=(int)(po.size())-i;j--)res*=po[j];
    				for(int k=(int)(ne.size())-1;k>=(int)(ne.size())-(5-i);k--)res*=ne[k];
    			}
    			else{
    				for(int j=0;j<i;j++)res*=po[j];
    				for(int k=0;k<5-i;k++)res*=ne[k];
    			}
    			ans=max(ans,res);
    		}
    	}
    	cout<<ans<<"
    ";
    }
    signed main(){
    	int testnum=1;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    洛谷题目页面传送门 & CF 题目页面传送门

    给定一棵大小为 (n) 的树。要求删掉一条边加上一条边使重心唯一。给出方案。本题多测。

    (ninleft[3,10^5 ight],sum nleq 10^5)

    假如说本来就唯一的话不用说。否则:

    显然两个重心相邻。钦定其中一个为根,那么另一个就是第 (2) 层。我也不知道怎么想出来的,就随便试吧,可以删掉第 (2) 层重心的任意一条通向儿子的边(由 (ngeq 3) 易证一定有儿子),然后把那个儿子连向根即可。

    证明的话,显然删加过之后根依然是重心,因为子树大小最大值变小了嘛。而第 (2) 层那个的相对应的显然变大了,那么它们的最大子树大小就不等了,就不可能同时为重心。然后如何证其他点不为重心呢?因为要想是重心就必须与根相邻,而这三个点发生关系跟其他儿子有个屁的关系,那最大子树大小肯定就不变啊,就无法翻身。得证。

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=100000;
    int n;
    vector<int> nei[N+1];
    int sz[N+1];
    void dfs(int x=1,int fa=0){
    	sz[x]=1;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa)continue;
    		dfs(y,x);
    		sz[x]+=sz[y];
    	}
    }
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)nei[i].clear();
    	for(int i=1;i<n;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		nei[x].pb(y);nei[y].pb(x);
    	}
    	dfs();
    	vector<int> cen;
    	for(int i=1;i<=n;i++){
    		bool flg=true;
    		int sum=1;
    		for(int j=0;j<nei[i].size();j++){
    			int x=nei[i][j];
    			if(sz[x]>sz[i])continue;
    			flg&=sz[x]<=n/2;
    			sum+=sz[x];
    		}
    		flg&=n-sum<=n/2;
    		if(flg)cen.pb(i);
    	}
    	assert(cen.size()<=2);
    	if(cen.size()==1)printf("%d %d
    %d %d
    ",1,nei[1][0],1,nei[1][0]);
    	else{
    		int son=nei[cen[0]][0]==cen[1]?nei[cen[0]][1]:nei[cen[0]][0];
    		printf("%d %d
    %d %d
    ",cen[0],son,cen[1],son);
    	}
    }
    int main(){
    	int testnum=1;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    D - Three Sequences

    洛谷题目页面传送门 & CF 题目页面传送门

    给定一个长度为 (n) 的数列 (a)。你需要构造出长度为 (n) 的数列 (b,c),满足 (a_i=b_i+c_i),且 (b) 不降,(c) 不升。最小化 (max(b_i,c_i))。然后还有 (q) 次区间增加,每次输出最小化的结果。

    (n,qinleft[1,10^5 ight])

    二话不说先找结论啊。通过观察样例发现,一开始 (b_1,c_1) 随便取只要满足 (a_1=b_1+c_1) 即可,然后以后的话,若 (a) 的增量 (geq 0) 就在 (b) 上加,否则就在 (c) 上减。证明的话随便想想很简单,显然 (max(b_i,c_i)=max(b_n,c_1)),那么在 (b_1,c_1) 固定的时候,(b) 的总增量显然越小越好,于是就只有在必要的时候才在 (b) 上加咯。

    然后现在解决 (b_1,c_1) 不固定的事情。我们想要令 (max(b_n,c_1)) 这个柿子最小,那么首先需要将 (b_n)(b_1) 表示一下。令增量 (Delta_i=a_{i+1}-a_i),则 (b_n=b_1+sumlimits_{i=1}^{n-1}[Delta_igeq 0]Delta_i)。令那个 (sum)(Sigma),那么柿子为 (max(b_1+Sigma,c_1))。又 (a_1=b_1+c_1),则柿子又可以写为 (max(b_1+Sigma,a_1-b_1))。那么注意到 (max) 两个参数是和为常量的,那么最理想的情况是令它们相等,则 (max) 最小。解个方程可以得到 (b_1=dfrac{a_1-Sigma}2)。但是不允许出现小数,所以还要取个整然后左右两边 round 一下。

    然后还有区间加呢。注意到区间加只会令两个增量变化,于是随便 (mathrm O(1)) 维护即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int inf=0x3f3f3f3f3f3f3f3f;
    const int N=100000;
    int n;
    int a[N+1];
    int qu;
    int d[N+1];
    int calc(int now,int x){
    	int res=inf;
    	for(int i=now-3;i<=now+3;i++)res=min(res,max(i+x,a[1]-i));
    	return res;
    }
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)scanf("%lld",a+i);
    	for(int i=1;i<n;i++)d[i]=a[i+1]-a[i];
    	int x=0;
    	for(int i=1;i<n;i++)if(d[i]>0)x+=d[i];
    	cout<<calc(a[1]-x>>1,x)<<"
    ";
    	cin>>qu;
    	while(qu--){
    		int l,r,v;
    		scanf("%lld%lld%lld",&l,&r,&v);
    		if(l==1)a[1]+=v;
    		if(l-1&&d[l-1]>0)x-=d[l-1];
    		if(r<n&&d[r]>0)x-=d[r];
    		d[l-1]+=v,d[r]-=v;
    		if(l-1&&d[l-1]>0)x+=d[l-1];
    		if(r<n&&d[r]>0)x+=d[r];
    		printf("%lld
    ",calc(a[1]-x>>1,x));
    	}
    }
    signed main(){
    	int testnum=1;
    //	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    E - Deleting Numbers

    这是钓鱼王子出的好题哦~

    洛谷题目页面传送门 & CF 题目页面传送门

    本题为交互题。给定 (n),初始时有 (A=[1,n]cap mathbb Z)。你有 (3) 种操作:

    1. 查询 (A) 中有多少个 (a) 的倍数((ain [1,n]));
    2. 查询 (A) 中有多少个 (a) 的倍数((ain[2,n])),并将 (A) 中所有 (a) 的倍数删去,特殊地,如果 (x)(a) 的倍数则不删;
    3. 得出答案 (a),操作之后立即结束。

    你需要在不超过 (10^4) 次操作内猜出某个预先设定好的 (xin A)

    (ninleft[2,10^5 ight])

    思路在于,通过操作 (1) 和操作 (2) 的配合,实现查询 (x) 是否是 (a) 的倍数。

    不难想到尝试将 (x) 分解质因数,根据弱智的唯一分解定理可以确定 (x)

    最 naive 的想法是每个质数的幂都试一遍。打个表发现 (pi(n)) 大概是 (9500+),然后质数的幂大概是 (9700) 左右个。你可能会说,噫,好,这场 div. 2 我阿克了!哦上帝,瞧瞧这天真的声音。注意到操作 (2) 是先返回结果再删除的,也就是说它的返回结果是个幌子,你想知道 ([amid x]) 必须要先 (2)(1)(2) 步走。

    想到寿司晚宴那题的一个结论:将质数分成小质数和大质数,则每个数最多含有一个大质因子。

    那么先将小质因子随便毛搞搞,我们设 ( au(m)) 表示 (m) 以内质数的幂的个数,那么是 (2 au(sqrt n)) 步的。实际上可以进一步优化,对于每个质数,先将它给 (2) 了,然后每个幂就可以直接查了。这样是 (pi(sqrt n)+ au(sqrt n)) 的。别看这一步优化微不足道,其实她能决定你是否 AC。

    现在把质因数分解式里的小质因子部分已经分解出来了,并且 (A) 里显然只剩下 (1)(x) 和所有大质数。接下来的任务就是要找出 (x) 是否有大质因子,如果有的话是谁。

    需要分出两种情况:

    1. 小质因子部分 (>1)。那么显然 (x) 是不属于那个大质数集的。那还怕个鬼啊,直接检查所有的大质数乘以小质因子部分是否剩一个数,剩的话大质因子就是他了乘上去,否则没有;
    2. 小质因子部分 (=1)。此时就需要害怕了,因为你「检查所有的大质数乘以小质因子部分是否剩一个数」的话,那你任何一次检查结果都是「是」,就无语了。而此时问题变得更加简单,剩下来的集合就是 (1) 和所有大质数,而你可以确定 (x) 就在里面。考虑对大质数序列分块。每块整体删一下,然后如果集合大小减少数量不对劲就说明 (x) 一定在这块里面,集中精力搞。由于块大小不大,可以直接逐个用操作 (1) 排查。总操作次数大约为 (pi(sqrt n)+ au(sqrt n)+(pi(n)-pi(sqrt n))+2sqrt{pi(n)-pi(sqrt n)}= au(sqrt n)+pi(n)+2sqrt{pi(n)-pi(sqrt n)}),卡的死死的,出题人真是毒瘤。
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    bool ispr(int x){
    	if(x<2)return false;
    	for(int i=2;i*i<=x;i++)if(x%i==0)return false;
    	return true;
    }
    int A(int x){
    	printf("A %d
    ",x),fflush(stdout);
    	cin>>x;return x;
    }
    int B(int x){
    	printf("B %d
    ",x);fflush(stdout);
    	cin>>x;return x;
    }
    void C(int x){printf("C %d
    ",x);fflush(stdout);exit(0);}
    void mian(){
    	int n;
    	cin>>n;
    	if(n==1)C(1);
    	vector<int> pr;
    	for(int i=2;i<=n;i++)if(ispr(i))pr.pb(i);
    	int x=1,lim=sqrt(n);
    	for(int i=0;pr[i]<=lim;i++){
    		B(pr[i]);
    		int now=1;
    		while(now*pr[i]<=n)now*=pr[i];
    		while(now>1){
    			if(A(now)==1){x*=now;break;}
    			now/=pr[i];
    		}
    	}
    	vector<int> v;
    	for(int i=0;i<pr.size();i++)if(pr[i]>lim)v.pb(pr[i]);
    	if(x==1){
    		int now=A(1);
    		for(int i=0;i<v.size();i+=100){
    			for(int j=i;j<i+100&&j<v.size();j++)B(v[j]);
    			int res=A(1);
    			if(now-res==min(i+100,int(v.size()))-i)now=res;
    			else{
    				for(int j=i;j<i+100&&j<v.size();j++)if(A(v[j])==1){x=v[j];break;}
    				break;
    			}
    		}
    	}
    	else{
    		for(int i=0;i<v.size();i++)if(1ll*x*v[i]<=n&&A(x*v[i])==1){x*=v[i];break;}
    	}
    	C(x);
    }
    int main(){
    	int testnum=1;
    //	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    739. Daily Temperatures
    556. Next Greater Element III
    1078. Occurrences After Bigram
    1053. Previous Permutation With One Swap
    565. Array Nesting
    1052. Grumpy Bookstore Owner
    1051. Height Checker
    数据库入门及SQL基本语法
    ISCSI的概念
    配置一个IP SAN 存储服务器
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-1406.html
Copyright © 2020-2023  润新知