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;
}