Codeforces Round #670 (Div. 2) solution

前言

又下飞分了…
来补一下题.

1406A - Subset Mex

把一些数分成两个可重集合 A , B A,B A,B.
最大化两个集合的 m e x ( ) mex() mex()之和.

贪心.
对于只出现一次的数,我们如果这个数在 A A A m e x mex mex值中能发挥作用,那么就不会丢到 B B B中.
因为一个的贡献为1,另一个的贡献 ≤ 1 \le 1 1.

int n,vis[110];
 
int main() {
	int _;qr(_); while(_--) {
		qr(n); memset(vis,0,sizeof vis);
		for(int i=1,x;i<=n;i++) qr(x),vis[x]++;
		int ans=0,x=0;
		while(vis[x]) vis[x++]--;
		ans+=x; x=0;
		while(vis[x]) vis[x++]--;
		ans+=x; pr2(ans);
	}	
	return 0;
}

1406B - Maximum Product

选5个不同的数,使他们的乘积最大.

直接 d p dp dp,因为有负数,所以记录一个最大值,一个最小值.
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个数选择 j j j个的最大值.
g g g为最小值.

int n,a[N];
ll f[N][7],g[N][7],ans;
 
int main() {
	int _;qr(_); while(_--) {
		qr(n);
		memset(f,-63,sizeof(f[0])*(n+1));
		memset(g,63,sizeof(g[0])*(n+1));
		ans=f[0][0];
		for(int i=1;i<=n;i++) {
			ll x;qr(x); 
			f[i][1]=max(f[i-1][1],(ll)x);
			g[i][1]=min(g[i-1][1],(ll)x);
			for(int j=2;j<=5&&j<=i;j++) {
				f[i][j]=max(f[i-1][j],max(f[i-1][j-1]*x,g[i-1][j-1]*x));
				g[i][j]=min(g[i-1][j],min(f[i-1][j-1]*x,g[i-1][j-1]*x));
			}
		}
		pr2(f[n][5]);
	}
	return 0;
}

1406C - Link Cut Centroids

给你一棵树,你需要删一条边加一条边使得重心唯一.

首先,一个重要条件是一棵树的重心至多为2且一定相邻.
那么我们只用考虑两个重心的情况:
我们设两重心为 x , y x,y x,y.那么我们把 x x x的儿子接一个到 y y y上面即可.
正确性证明: y y y的最大子树 < n / 2 <n/2 <n/2.其他点如果为重心,那么最大子树 ≥ n − m a x s i z e ( s o n ( y ) ) ≥ n / 2 \ge n-maxsize(son(y))\ge n/2 nmaxsize(son(y))n/2.

int T,n,f[N],sz[N],fa[N],u,v;
struct edge{int y,next; } a[N*2]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; }

void dfs(int x) {
	sz[x]=f[x]=1;
	for(int k=last[x],y;k;k=a[k].next) 
		if((y=a[k].y)^fa[x]) {
			fa[y]=x; dfs(y);
			sz[x] += sz[y];
			f[x]=max(f[x],sz[y]);
		}
	f[x]=max(f[x],n-sz[x]);
}

int main() {
	qr(T); while(T--) {
		qr(n); len=1; 
		for(int i=1;i<=n;i++) last[i]=0;
		for(int i=1,x,y;i<n;i++) qr(x),qr(y),ins(x,y),ins(y,x);
		fa[1]=0; dfs(1); u=1; v=0;
		for(int i=2;i<=n;i++) 
			if(f[u]>f[i]) u=i,v=0;
			else if(f[u] == f[i]) v=i;
		if(!v) {
			pr1(2); pr2(fa[2]);
			pr1(2); pr2(fa[2]);
		}
		else {
			fa[u]=0; dfs(u);
			for(int k=last[u];k;k=a[k].next)
				if(a[k].y^v) {
					int x=a[k].y;
					pr1(x); pr2(u);
					pr1(x); pr2(v);
					break;
				} 
		}
	}
	return 0;
}

1406D - Three Sequences

给定 a a a,求出一个非降序列 b b b和一个非升序列 c c c.
满足 a i = b i + c i a_i=b_i+c_i ai=bi+ci.最小化 max ⁡ ( b i , c i ) \max(b_i,c_i) max(bi,ci).

我们差分一下.
先令 b 1 = a 1 , c 1 = 0 b_1=a_1,c_1=0 b1=a1,c1=0.
然后上升就改 b b b,否则改 c c c.
我们统计一下 max ⁡ ( d i , 0 ) \max(d_i,0) max(di,0)即为 c c c.
然后取一下 b 1 , c n b1,c_n b1,cn中位数即可.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=1e5+10,size=1<<20,mod=(int)1e9+7,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

int n,m;
ll d[N],ans;

void A() {
	ans += d[1];
	if(ans>=0) pr2((ans+1)/2);
	else pr2(ans/2);
	ans -= d[1];
}

int main() {
	qr(n); 
	for(int i=1;i<=n;i++) qr(d[i]);
	for(int i=n;i>1;i--) ans += max(0LL,d[i] -= d[i-1]);
	A();qr(m); while(m--) {
		int l,r,x; qr(l); qr(r); qr(x);
		if(l>1) ans -= max(d[l],0LL);
		ans -= max(d[r+1],0LL);
		d[l] += x;
		if(l>1) ans += max(d[l],0LL);
		if(r<n) ans += max(d[r+1] -= x,0LL);
		A();
	}
	return 0;
}

1406E - Deleting Numbers

比赛的时候,看 C , D C,D C,D不太会,就跑来淦 E E E,结果最后还没搞出来.
我觉得败笔在于不太会调交互题,只会提交调试法.
其实只要询问操作的时候,在函数内自行交互即可.(就是把答案先读入,然后交互围绕答案进行).

题意:给定 n n n,初始集合为 [ 1 , n ] [1,n] [1,n],你需要猜数.有以下3种操作:

  1. x x x. 查询集合 x x x的倍数个数.
  2. x x x. 执行完 1 1 1后,删除所有倍数.但是要猜的数并不会删除.
  3. x x x.找到答案 x x x.

n ≤ 1 e 5 n\le 1e5 n1e5,交互次数为 10000 10000 10000.

我们先猜一下 10000 10000 10000的实际意义.
由于 n n n以内的质数个数大约 9500 9500 9500.
所以我们要每个质数均摊一次询问.

答案有3种情况: 1 1 1,质数,合数.

先处理最简单的合数.
显然最小质因子至多为 n \sqrt n n .
所以我们先查询 n \sqrt n n 内的所有质因子的指数.
初始令 r e s = 1 res=1 res=1.(表示答案)
查询细节:

  1. B ( p ) B(p) B(p).
  2. 再来一次 B ( p ) B(p) B(p),如果 B ( p ) = 1 B(p)=1 B(p)=1,说明 p ∣ a n s → r e s = r e s ⋅ p p|ans\rightarrow res=res\cdot p pansres=resp.
  3. 尝试 B ( p 2 ) B(p^2) B(p2),如果 B ( p 2 ) = 1 B(p^2)=1 B(p2)=1, r e s = r e s ⋅ p res=res\cdot p res=resp.
  4. 尝试 B ( p 3 ) B(p^3) B(p3)

这样每个质因子都要查2次,然后就是加上 a n s ans ans的质因子个数 log ⁡ n \log n logn.
因为这个质因子 p ≤ n p\le \sqrt n pn ,所以只有几十个.
两者对询问次数的影响很小.

然后 a n s ans ans可能还有一个 > n >\sqrt n >n 的质因子.
所以我们遍历所有 > n >\sqrt n >n 的质因子 p p p.
如果 A ( p ) > 1 A(p)>1 A(p)>1,那么就一定有这个质因子.

剩下的情况就是 > n >\sqrt n >n 的质数和 1 1 1.
我们只要排除质数的情况即可.
找质数我们考虑分治.
对于 [ l , r ] [l,r] [l,r]内的质数我们先对一半的质数 B ( p ) ( p ∈ [ l , m i d ] ) ) B(p)(p\in[l,mid])) B(p)(p[l,mid])).
然后 A ( 1 ) A(1) A(1)一下,看看和预估的答案是否一致.
如果不一致,那么答案就在 [ l , m i d ] [l,mid] [l,mid]中,再扫一遍即可.
否则递归处理 [ m i d + 1 , r ] [mid+1,r] [mid+1,r].
可以发现这个询问次数是质因子个数.

代码中有#define op false,这个是用来调试的.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair<int,int>
#define fi first
#define se second
#define pb push_back
#define IT iterator
#define vi vector<int>
#define vl vector<ll>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=1e5+10,size=1<<20,mod=998244353,inf=2e9;

template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
	while(isdigit(c)) x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); puts("");
}


int n,prime[N],tot,a[N],cnt; bool v[N];
ll ans,res;

#define op false
bool vis[N];

int A(int x) {
	if(op) {
		int s=0;
		for(register int i=x;i<=n;i+=x) s += vis[i];
		return s; 
	}
	printf("A %d\n",x); fflush(stdout);
	qr(x); return x;
}

int B(int x) {
	if(op) {
		int s=0;
		for(register int i=x;i<=n;i+=x) s += vis[i],vis[i]=0;
		vis[res]=1;
		return s;
	}
	printf("B %d\n",x); fflush(stdout);
	qr(x); return x;
}

void C() {printf("C %lld\n",ans); exit(0);}

int last;
void solve(int l,int r) {
	if(l > r) return ;
	if(l == r) {
		B(a[l]);
		if(B(a[l])) ans *= a[l];//因子至多有一个.
		return ;
	}
	int mid=(l+r)/2;
	for(int i=l;i<=mid;i++) B(a[i]);
	last -= mid-l+1;
	if(A(1)^last) {
		for(int i=l;i<=mid;i++)
			if(B(a[i])) ans *= a[i],C();
	}
	solve(mid+1,r);
}

int main() {
	qr(n); int m=sqrt(n)+1; ans=1;
	if(op) {
		qr(res);
		for(int i=1;i<=n;i++) vis[i]=1;
	}
	for(int i=1;i<=n;i++) v[i]=1;
	for(int i=2;i<=n;i++) if(v[i]) {
		prime[++tot]=i; 
		if(i>m) a[++cnt]=i;
		else {
			B(i);
			if(B(i)) {
				ans *= i;
				for(ll x=i*i;ans*i<=n;x*=i)
					if(B(x)) ans *= i;
					else break;
			}
			if(ans*i>n) C();
		}
		for(ll j=(ll)i*i;j<=n;j+=i) v[j]=0;
	}
	if(ans>1) {
		for(int i=1;i<=cnt;i++)
			if(A(a[i])>1) ans *= a[i],C();
	}
	else last=A(1),solve(1,cnt);
	C();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Infinite_Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值