Pbri

Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) 题解

Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) 题解

ps:F莫得题解俺实在不会做/kk

2A

按照题意模拟,每模拟一遍检查一下就好了。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 1010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int T;
int n,a[MAXN],re[MAXN];
void solve()
{
	n=read();
	FUP(i,1,n) a[i]=re[i]=read();
	sort(re+1,re+n+1);
	FUP(i,1,INF)
	{
		bool pd=false;
		FUP(j,1,n)
		{
			if(a[j]!=re[j])
			{
				pd=true;
				break;
			}
		}
		if(!pd)
		{
			printf("%d\n",i-1);
			return;
		}
		if(i&1)
		{
			for(int j=1;j<=n-2;j+=2)
			{
				if(a[j]>a[j+1])
				{
					swap(a[j],a[j+1]);
				}
			}
		}
		else
		{
			for(int j=2;j<=n-1;j+=2)
			{
				if(a[j]>a[j+1])
				{
					swap(a[j],a[j+1]);
				}
			}
		}
	}
}
int main(){
	T=read();
	while(T--) solve();
	return 0;
}

2B/1A

本质是问你有 \(a+b\) 位的 \(101010...\)\(a\) 串,然后你要构建一个有 \(a\)\(1\)\(b\)\(0\)\(b\) 串,问异或之后可能有多少个位置是 \(1\) ,然后把 \(a,b\) 反过来再做一遍就好。这个你先考虑最少的情况,就是前面先 \(101010...\) 交叉的摆,后面某一个数字不够之后就把另一种剩下的全摆上去,这时候就是最少的答案。然后你发现你每次交换两个位置要么没有变化要么多了 \(2\) 位,所以你把前面摆放方式改成 \(010101...\) 就是最多的情况,然后你这中间每隔 \(1\) 个数都是可能出现的答案。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
bool ok[MAXN];
void color(int a,int b)
{
	int n=a+b;
	if(a>b)
	{
		int s=(n-2*b)/2;
		int t=s+2*b;
		for(int i=s;i<=t;i+=2) ok[i]=true;
	}
	else
	{
		int s=(n-2*a+1)/2;
		int t=s+2*a;
		for(int i=s;i<=t;i+=2) ok[i]=true;
	}
}
int T,a,b;
void solve()
{
	a=read(),b=read();
	FUP(i,0,a+b) ok[i]=0;
	color(a,b);
	color(b,a);
	int cnt=0;
	FUP(i,0,a+b) cnt+=ok[i];
	printf("%d\n",cnt);
	FUP(i,0,a+b) if(ok[i]) printf("%d ",i);
	puts("");
}
int main(){
	T=read();
	while(T--) solve();
	return 0;
}

2C

对每个洞穴考虑最少需要多少,做法是二分一个答案然后去模拟。然后按照最少需要多少这个值从小到大排序,然后再二分答案,继续模拟就好了。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int T,n;
struct cave{
	int w,val;
	vector<int>d;
}p[MAXN];
bool cmp(cave x,cave y){return x.w<y.w;}
bool ck(int x,vector<int>vc)
{
	for(auto i:vc)
	{
		if(x<=i) return false;
		x++;
	}
	return true;
}
void getdata(int id)
{
	int l=1,r=1e9+1,re=1e9+1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(ck(mid,p[id].d)) r=mid-1,re=mid;
		else l=mid+1;
	}
	p[id].w=re;
}
bool ck2(int x)
{
	FUP(i,1,n)
	{
		if(x<p[i].w) return false;
		x+=p[i].val;
	}
	return true;
}
void solve()
{
	FUP(i,1,n)
	{
		p[i].w=p[i].val=0;
		p[i].d.clear();
	}
	n=read();
	FUP(i,1,n)
	{
		p[i].val=read();
		FUP(j,0,p[i].val-1) p[i].d.push_back(read());
	}
	FUP(i,1,n) getdata(i);
	//FUP(i,1,n) printf("i=%d w=%d val=%d\n",i,p[i].w,p[i].val)
	sort(p+1,p+n+1,cmp);
	int l=1,r=1e9+1,ans=1e9+1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(ck2(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%d\n",ans);
}
int main(){
	T=read();
	while(T--) solve();
	return 0;
}

2D/1B

枚举倍数,除 \(j\) 下取整是 \(i\) 的数是 \([i\times j,(i+1)\times j]\) 内的数

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 4000010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int n,MOD;
ll S[MAXN],dp[MAXN];
ll sum(int l,int r)
{
	return (S[l]-S[r+1]+MOD)%MOD;
}
int main(){
	n=read(),MOD=read();
	dp[n]=S[n]=1;
	FDW(i,n-1,1)
	{
		dp[i]=S[i+1];
		FUP(j,2,n/i)
		{
			int l=i*j,r=min(n,j*(i+1)-1);
			//printf("i=%d j=%d l=%d r=%d\n",i,j,l,r);
			dp[i]=dp[i]+sum(l,r);
			if(dp[i]>=MOD) dp[i]-=MOD;
		}
		//printf("i=%d dp=%lld\n",i,dp[i]);
		S[i]=S[i+1]+dp[i];
		if(S[i]>=MOD) S[i]-=MOD;
	}
	printf("%lld\n",dp[1]);
	return 0;
}

2E/1C

考虑一次翻转并不改变他所在位置的奇偶性,所以可以根据这个把无解判掉。接下来考虑从后往前填(这样可以不改变已经填好的地方),因为偶数不能自己翻过去,所以一定是一个奇数配合一个偶数一起翻过去。设奇数 \(i\) 在的位置是 \(p_1\) ,那么先 \(rev(p_1)\) ,然后设目前 \(i-1\)\(p_2\) ,那么按照这个操作顺序: \(rev(p_2-1)\rightarrow rev(p_2+1)\rightarrow rev(3)\rightarrow rev(i)\) ,加上一开始的 \(rev(p_1)\) 正好五次。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int T,n,a[MAXN];
vector<int>re;
void rev(int p){for(int x=1,y=p;x<y;x++,y--) swap(a[x],a[y]);}
void solve()
{
	re.clear();
	n=read();
	FUP(i,1,n) a[i]=read();
	FUP(i,1,n)
	{
		if(a[i]%2!=i%2)
		{
			puts("-1");
			return;
		}
	}
	for(int i=n;i>=3;i-=2)
	{
		int p;
		FUP(j,1,i) if(a[j]==i) p=j;
		re.push_back(p),rev(p);
		FUP(j,1,i) if(a[j]==i-1) p=j;
		re.push_back(p-1),rev(p-1);
		re.push_back(p+1),rev(p+1);
		re.push_back(3),rev(3);
		re.push_back(i),rev(i);
	}
	printf("%d\n",(int)(re.size()));
	for(auto i:re) printf("%d ",i);
	puts("");
}
int main(){
	T=read();
	while(T--) solve();
	return 0;
}

2F/1D

考虑排好序后的序列 \({b_n}\) ,一定有 \(b_i\ge b_{i-1}\) ,然而对于一次插入,一定是满足严格小于时才会插入,所以有些位置是 \(b_i>b_{i-1}\) ,假设我们已经知道了有 \(c\) 个这样的位置,那么答案就应该是 \(\dbinom{2n-c-1}{n}\) 。证明是因为在排好序的序列中,我们最大可能出现的数就是 \(n\) ,然后我们考虑说对于所以 \(b_i\ge b_{i-1}\) 的间隔,我们都让 \(b_i\leftarrow b_i+1\) ,这样我们就构造了一个从一个 \([1,n]\) 的不降序列到 \([1,2n-c-1]\) 的严格上升序列的映射。同时,我们把那个严格上升序列的每一个在 \(b_i\ge b_{i-1}\) 的位置上的数都减一,就回到了原来的序列,也就是说我们构造了一个双射,所以只需要计数严格上升序列的个数就好了,显然就是从 \(2n-c-1\) 个数中挑 \(n\) 个按顺序摆就好了。然后考虑怎么求 \(c\) ,我们在每个插入的 \(y\) 那个数上面打标记就好了(注意不是那个位置,这个标记会随着以后的插入而变动),插入我们用 \(Splay\) 模拟就好了。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <stack>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 998244353
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int Test;
stack<pr >OP;
#define ls(p) T[p].son[0]
#define rs(p) T[p].son[1]
#define fa(p) T[p].fa
#define siz(p) T[p].siz
#define tag(p) T[p].tag
#define root rs(0)
struct node{
	int siz,fa,son[2];
	bool tag,is;
}T[MAXN];
void push_up(int p){siz(p)=siz(ls(p))+siz(rs(p))+1;}
void connect(int p,int f,int d){fa(p)=f,T[f].son[d]=p;}
int getson(int p){return rs(fa(p))==p;}
int build(int l,int r,int f)
{
	int p=(l+r)>>1;
	if(!f) root=p;
	fa(p)=f,siz(p)=1;
	if(l<p) ls(p)=build(l,p-1,p);
	if(r>p) rs(p)=build(p+1,r,p);
	push_up(p);
	return p;
}
void push_down(int p)
{
	if(tag(p))
	{
		if(ls(p)) swap(ls(ls(p)),rs(ls(p))),tag(ls(p))^=1;
		if(rs(p)) swap(ls(rs(p)),rs(rs(p))),tag(rs(p))^=1;
		tag(p)=0;
	}
}
void ro(int p)
{
	int f=fa(p),ff=fa(f);
	int d1=getson(p),d2=getson(f);
	connect(T[p].son[d1^1],f,d1);
	connect(f,p,d1^1);
	connect(p,ff,d2);
	push_up(f),push_up(p);
}
void splay(int p,int goal)
{
	goal=fa(goal);
	while(fa(p)!=goal)
	{
		int f=fa(p),ff=fa(f);
		int d1=getson(p),d2=getson(f);
		if(ff==goal)
		{
			ro(p);
			return;
		}
		ro(d1==d2?f:p);
		ro(p);
	}
}
int kth(int k)
{
	int p=root;
	//printf("root=%d siz=%d\n",root,siz(root));
	while(1)
	{
		push_down(p);
		if(siz(ls(p))+1==k) return p;
		int d=k>siz(ls(p));
		if(d) k-=siz(ls(p))+1;
		p=T[p].son[d];
	}
}
void Rev(int ql,int qr)
{
	qr+=2;
	ql=kth(ql),qr=kth(qr);
	splay(ql,root),splay(qr,rs(root));
	int p=ls(rs(root));
	tag(p)^=1,swap(ls(p),rs(p));
}
void update(int x,int y)
{
	int p=kth(x+1);
	splay(p,root);
	T[p].is=y;
}
int query(int x)
{
	int p=kth(x+1);
	//printf("p=%d,x+1=%d\n",p,x+1);
	splay(p,root);
	return T[p].is; 
}

void getback()
{
	int cnt=OP.size();
	while(!OP.empty())
	{
		cnt++;
		pr opt=OP.top();
		OP.pop();
		Rev(opt.fi,opt.se);
		if(!(cnt&1)) update(opt.fi,0);
	}
}
ll poww(ll a,int b)
{
	ll ans=1,base=a;
	while(b)
	{
		if(b&1) ans=ans*base%MOD;
		base=base*base%MOD;
		b>>=1;
	}
	return ans;
}
ll fac[MAXN*2],invfac[MAXN*2];
void init()
{
	build(1,2e5+2,0);
	fac[0]=1;
	FUP(i,1,4e5) fac[i]=fac[i-1]*i%MOD;
	invfac[(int)4e5]=poww(fac[(int)4e5],MOD-2);
	FDW(i,4e5-1,0) invfac[i]=invfac[i+1]*(i+1)%MOD;
}
ll C(int x,int y){return fac[x]*invfac[y]%MOD*invfac[x-y]%MOD;}
int n,m,ans;
void solve()
{
	n=read(),m=read(),ans=0;
	FUP(i,1,m)
	{
		int x=read(),y=read();
		int tmp=query(y);
		//printf("tmp=%d\n",tmp);
		if(!tmp)
		{
			ans++;
			update(y,1);
		}
		Rev(y,x);
		OP.push(mkp(y,x));
		Rev(y+1,x);
		OP.push(mkp(y+1,x));
	}
	//printf("ans=%d\n",ans);
	printf("%lld\n",C(2*n-ans-1,n));
	getback();
}
int main(){
	init();
	Test=read();
	while(Test--) solve();
	return 0;
}

1E

先吹强少码风!考虑二分答案,转化成验证性问题。我们维护一个集合 \(S\) 表示目前可以到达的地方,初始化为 \(\{1\}\) ,显然这个集合内的点可以互相到达。然后再去扩展这个集合。我们设 \(u,v\) 是两个集合内的点,那么如果存在一条打怪的路径是 \(u\rightarrow p_1\rightarrow p_2\rightarrow...\rightarrow p_k\rightarrow v\) ,那么显然我们就可以把 \(p_1,p_2...p_k\)\(k\) 个点加入集合 \(S\) 。再考虑如果我们目前走的路径要走一个经过过的点,这个点有可能是目前自己路径上的,也可能是以前尝试过的,那么我们一定可以走这个点。证明:如果这个点是自己目前路径上的,说明目前已经经过这个点,肯定可以走这个点。如果是以前尝试过的,那么说明目前在的这个点在之前这条路径里被尝试过但没有走,因为这个点太大了走不了。然而现在已经走到这个点了,说明它可以沿着之前的路径走回去。所以可以把要走的点和目前在的点到 \(S\) 的路径上的点都加入 \(S\)

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 1010
#define MAXM 4010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int n,m,cnt,fr[MAXN];
ll a[MAXN],b[MAXN],ans,cur;
bool vis[MAXN];
int head[MAXN],ednum;
struct edge{
	int nxt,to;
}ed[MAXM];
void add_Edge(int u,int v)
{
	ednum++;
	ed[ednum].nxt=head[u],ed[ednum].to=v;
	head[u]=ednum;
}
void clean()
{
	ednum=ans=0;
	memset(head,0,sizeof(head));
	memset(ed,0,sizeof(ed));
}
void init()
{
	n=read(),m=read();
	FUP(i,2,n) a[i]=read();
	FUP(i,2,n) b[i]=read();
	FUP(i,1,m)
	{
		int u=read(),v=read();
		add_Edge(u,v),add_Edge(v,u);
	}
}
void update(int u)
{
	while(!vis[u])
	{
		vis[u]=true,cnt++,cur+=b[u];
		u=fr[u];
	}
}
bool dfs(int u,int fa,ll val)
{
	FED(i,u)
	{
		int v=ed[i].to;
		if(v==fa||val<=a[v]) continue;
		if(!vis[u]&&vis[v])
		{
			update(u);
			return true;
		}
		if(vis[v]) continue;
		if(fr[v])
		{
			update(v),update(u);
			return true;
		}
		fr[v]=u;
		if(dfs(v,u,val+b[v])) return true;
	}
	return false;
}
void work()
{
	ll l=1,r=1e9+1;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		memset(vis,0,sizeof(vis));
		bool ok=false;
		vis[1]=true,cur=mid,cnt=1;
		while(cnt<n)
		{
			ok=false;
			FUP(i,1,n) fr[i]=0;
			FUP(i,1,n)
			{
				if(vis[i])
				{
					if(dfs(i,0,cur))
					{
						ok=true;
						break;
					}
				}
			}
			if(!ok&&cnt<n) break;
		}
		if(!ok) l=mid+1;
		else r=mid-1,ans=mid;
	}
	printf("%lld\n",ans);
}
void solve()
{
	clean();
	init();
	work();
}
int main(){
	int T=read();
	while(T--) solve();
	return 0;
}
posted @ 2021-08-25 22:03  Pbri  阅读(53)  评论(0编辑  收藏  举报