Codeforces Round #670 (Div. 2) 题解

A

简单的贪心,就直接上代码了:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 110

int T,n,a[maxn],b[maxn];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]++;
		int ans=0,now=0;
		while(b[now]>1)ans+=2,now++;
		while(b[now]>0)ans++,now++;
		printf("%d\n",ans);
	}
}

B

m i [ i ] , m a [ i ] mi[i],ma[i] mi[i],ma[i] 表示枚举到当前位置,用 i i i 个数能够乘出的最小和最大数,他们的转移只和 m i [ i − 1 ] , m a [ i − 1 ] mi[i-1],ma[i-1] mi[i1],ma[i1] 相关,瞎搞搞就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 200010
#define ll long long

int T,n;
ll a[maxn],mi[10],ma[10];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);ll ans=-1e18;
		for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
		for(int i=1;i<=5;i++)mi[i]=1e18,ma[i]=-1e18;
		for(int i=1;i<=n;i++){
    
    
			for(int j=5;j>=2;j--)if(i>=j){
    
    
				mi[j]=min(mi[j],min(mi[j-1]*a[i],ma[j-1]*a[i]));
				ma[j]=max(ma[j],max(mi[j-1]*a[i],ma[j-1]*a[i]));
			}
			mi[1]=min(mi[1],a[i]);
			ma[1]=max(ma[1],a[i]);
			ans=max(ans,ma[5]);
		}
		printf("%lld\n",ans);
	}
}

C

看一下一开始几个重心,如果有两个,那么把其中一个重心的一个叶子给另一个重心,这样重心就唯一了。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 200010
#define pb push_back

int T,n;
vector<int>e[maxn];
int size[maxn],mson[maxn],rt,rt_;
void dfs(int x,int fa){
    
    
	size[x]=1;mson[x]=0;
	for(int y:e[x])if(y!=fa){
    
    
		dfs(y,x);size[x]+=size[y];
		if(size[y]>mson[x])mson[x]=size[y];
	}
	if(n-size[x]>mson[x])mson[x]=n-size[x];
	if(mson[x]<mson[rt])rt=x,rt_=0;
	else if(mson[x]==mson[rt])rt_=x;
}
void dfs1(int x,int fa,int &l,int &f){
    
    
	int son=e[x].size()-1;
	if(!son)l=x,f=fa;
	else if(e[x][0]!=fa)dfs1(e[x][0],x,l,f);
	else dfs1(e[x][1],x,l,f);
}

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		for(int i=1;i<=n;i++)e[i].clear();
		for(int i=1,x,y;i<n;i++)scanf("%d %d",&x,&y),e[x].pb(y),e[y].pb(x);
		mson[rt=rt_=0]=1e9;dfs(1,0);
		if(rt_){
    
    
			int l,f;
			dfs1(rt,rt_,l,f);
			printf("%d %d\n%d %d\n",l,f,l,rt_);
		}else{
    
    
			printf("%d %d\n%d %d\n",1,e[1][0],1,e[1][0]);
		}
	}
}

D

先考虑如何将一个序列拆成一个不下降和一个不上升的序列。考虑只做减法,让这个序列变成不上升序列,那么就是要将每一个极长不下降子段整体减小,直到最大值等于上一个子段的最小值。

那么每个位置减去的值,一定能够形成一个不下降子序列,两个序列就构造完了。然后还要使他们的最大值最小,令 a , b a,b a,b 为两个序列当前的最大值,显然可以让大的那个整体减小,小的整体增加,使得最大值为 ⌈ a + b 2 ⌉ \lceil \frac {a+b} 2\rceil 2a+b

不上升子序列的最大值显然就是第一位上的数,而不下降子序列最大值是该序列最后一位上的数,令原序列做差分得到 b b b 数组,那么最后一位上的数就是所有满足 b [ i ] > 0 b[i]>0 b[i]>0 b [ i ] b[i] b[i] 之和。

修改的话直接修改差分数组,可以 O ( 1 ) O(1) O(1) 完成。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long

int n,m;
ll a[maxn],b[maxn],sum=0;

int main()
{
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),b[i]=a[i]-a[i-1];
	for(int i=2;i<=n;i++)if(b[i]>0)sum+=b[i];
	printf("%lld\n",(ll)ceil(1.0*(b[1]+sum)/2.0));
	scanf("%d",&m);
	for(int i=1,l,r,x;i<=m;i++){
    
    
		scanf("%d %d %d",&l,&r,&x);
		if(l>1&&b[l]>0)sum-=b[l];
		b[l]+=x;
		if(l>1&&b[l]>0)sum+=b[l];
		if(r<n){
    
    r++;
			if(b[r]>0)sum-=b[r];
			b[r]-=x;
			if(b[r]>0)sum+=b[r];
		}
		printf("%lld\n",(ll)ceil(1.0*(b[1]+sum)/2.0));
	}
}

E

发现 1 0 5 10^5 105 以内的质数有 9592 9592 9592 个,这意味着每个质数大约只能操作 1 1 1 次。

先枚举 n \sqrt n n 以内的质数,将 x x x 质因数分解后这些质数的出现次数可能超过 1 1 1,每次暴力 B B B 一下枚举到的 p p p,然后枚举 p , p 2 , p 3 , . . . p,p^2,p^3,... p,p2,p3,... 依次做 A A A 操作,这样可以确定他们的出现次数。

再考虑大于 n \sqrt n n 的质数,这些质数最多出现一次,并且此时在 n \sqrt n n ~ n n n 范围内已经没有合数了(除了 x x x)。

先考虑 x x x 是大于 n \sqrt n n 的质数,每次 B B B 一半的数,如果 B B B 完之后询问 A   1 A~1 A 1 和预期的不一样,那么这之中一定有 x x x,每个 A A A 一下就能找到,否则就在另外一半里面,重复上面的操作,可以发现这样操作需要的次数大概就是质数个数次的。

再考虑 x x x 拥有大于 n \sqrt n n 的质数作为因子,但不是质数的情况,那么在上面 B B B 的过程中,看看是否某一次 B B B 出了 2 2 2 就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int n,ans=1;
int prime[maxn],t=0;
bool v[maxn];
void work(){
    
    
	for(int i=2;i<=n;i++){
    
    
		if(!v[i])prime[++t]=i;
		for(int j=1;j<=t&&i*prime[j]<=n;j++){
    
    
			v[i*prime[j]]=true;
			if(i%prime[j]==0)break;
		}
	}
}
int ask(char x,int y){
    
    
	printf("%c %d\n",x,y);fflush(stdout);
	if(x!='C'){
    
    int re;scanf("%d",&re);return re;}
	else exit(0);
}
int st=1,sum;
void solve(int l,int r){
    
    
	int mid=l+r>>1;
	for(int i=l;i<=mid;i++){
    
    
		if(ask('B',prime[i])>1)ask('C',ans*prime[i]);
		sum--;
	}
	if(ask('A',1)!=sum){
    
    
		for(int i=l;i<=mid;i++)
		if(ask('A',prime[i]))ask('C',ans*prime[i]);
	}
	if(mid<r)solve(mid+1,r);
}

int main()
{
    
    
	scanf("%d",&n);work();sum=n;
	for(;st<=t&&prime[st]*prime[st]<=n;st++){
    
    
		int &x=prime[st];
		sum-=ask('B',x);
		if(ask('A',1)!=sum){
    
    
			sum++;
			for(int j=x;j<=n&&ask('A',j);j*=x)ans*=x;
		}
	}
	if(st<=t)solve(st,t);
	ask('C',ans);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/108564129