title :2022牛客暑期多校训练营10 题解
date : 2022-9-26
tags : ACM,练习记录
author : Linno
2022牛客暑期多校训练营10 题解
题目链接 :https://ac.nowcoder.com/acm/contest/33195
补题进度 :5/11 ( B E F H I)
提醒我补题QAQ
B-Fall Guys-Perfect Match
二分切比雪夫距离,枚举颜色将其扩展成若干矩形之后取并集,然后将所有颜色的并取交集就能求出当前答案所需的最大覆盖。
#include<bits/stdc++.h>
using namespace std;
int solve(int n,int m,int k,const vector<vector<int>>&pos){
vector<vector<int>>d;
map<int,int>s;
int maxA=pos.size();
d.assign(n,vector<int>(m));
for(int v=0;v<maxA;++v){
s.clear();
s[0]=0;
s[m]=-1;
auto split=[&](int x){
s[x]=prev(s.upper_bound(x))->second;
};
for(int p:pos[v]){
int x=p/m,y=p%m;
int u=max(0,x-k),l=max(0,y-k);
x=min(x+k+1,n);
y=min(y+k+1,m);
split(l);split(y);
for(auto it=s.find(l);it->first<y;){
int up=max(it->second,u);
int lf=it->first;
it=s.erase(it);
int rt=it->first;
if(up<m) ++d[up][lf];
if(up<m&&rt<m) --d[up][rt];
if(x<n) --d[x][lf];
if(rt<m&&x<n) ++d[x][rt];
}
s[l]=x;
}
}
for(int i=0;i<n;++i){
for(int j=1;j<m;++j) d[i][j]+=d[i][j-1];
}
for(int i=1;i<n;++i){
for(int j=0;j<m;++j) d[i][j]+=d[i-1][j];
}
int mx=0,t=(n+1)/2;
for(int i=0;i<t;++i){
for(int j=0;j<t;++j){
int x=i+j,y=j-i+t-1;
mx=max(mx,d[x][y]);
}
}
return mx;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m;
cin>>n>>m;
int N=2*n-1;
vector<vector<int>>a(n,vector<int>(n));
vector<vector<int>>pos(m);
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
cin>>a[i][j];
--a[i][j];
pos[a[i][j]].emplace_back((i+j)*N+(j-i+n-1));
}
}
for(int i=0;i<m;++i) sort(pos[i].begin(),pos[i].end());
int L=0,R=n;
while(L<R){
int x=(L+R)/2;
if(solve(N,N,x,pos)==m) R=x;
else L=x+1;
}
cout<<L<<"\n";
}
E-Reviewer Assignment
看范围建图跑费用流,对于多次审稿,只需要每篇论文向汇点连多条边,并将费用设为0,1,2…,n为止,这样可以保证每篇论文能尽可能被更多人审核。
#include<bits/stdc++.h>
//#define int long long
#define mk make_pair
#define pii pair<int,int>
#define inf 0x3f3f3f3f
using namespace std;
const int N=402,M=2e6+7;
int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,m,match[N],cnt[N];
bool vis[N];
vector<int>G[N];
bool dfs(int x){
for(auto to:G[x]){
if(vis[to]) continue;
vis[to]=1;
if(!match[to]||dfs(match[to])){
match[to]=x;
return 1;
}
}
return 0;
}
signed main(){
n=read();m=read();
char str[N];
bool flag=1;
for(int i=1;i<=n;++i){
scanf("%s",str+1);
bool hs=0;
for(int j=1;j<=m;++j){
if(str[j]=='1') G[j].emplace_back(i),hs=1;
}
flag&=hs;
}
if(!flag){
puts("-1");
return 0;
}
int num=n;
while(num){
for(int i=1;i<=m;++i){
memset(vis,0,sizeof(vis));
num-=dfs(i);
}
}
for(int i=1;i<=n;++i) cout<<match[i]<<" ";
return 0;
}
F-Shannon Switching Game?
显然两点 s − > t s->t s−>t能够实现移动的条件是存在重边,模拟拓扑的过程,从t倒过来找必胜态即可。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
//#define int long long
using namespace std;
typedef long long ll;
const int N=207;
const int mod=1e9+7;
//int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,m,s,t;
int is[N],cnt[N],deg[N],vis[N];
vector<int>G[N];
void Solve(){
cin>>n>>m>>s>>t;
for(int i=1,u,v;i<=m;++i){
cin>>u>>v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
queue<int>q;
q.emplace(t);
is[t]=1;vis[t]=1;
while(q.size()){
int fro=q.front();
q.pop();
for(auto to:G[fro]){
if(vis[to]) continue;
++cnt[to];
if(cnt[to]>=2) is[to]=1;
if(is[to]) vis[to]=1,q.emplace(to);
}
}
if(is[s]) cout<<"Join Player\n";
else cout<<"Cut Player\n";
for(int i=1;i<=n;++i){
G[i].clear();
is[i]=deg[i]=cnt[i]=vis[i]=0;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
return 0;
}
H-Wheel of Fortune
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+7;
const int mod=998244353;
typedef unsigned long long ull;
int frac[N],ifrac[N],a[10],b[10];
inline int fpow(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
inline int inv(int x){
return fpow(x,mod-2);
}
inline int C(int n,int m){
if(n<m) return 0;
return frac[n]*ifrac[m]%mod*ifrac[n-m]%mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int sz=3e6;
frac[0]=1;for(int i=1;i<=sz;++i) frac[i]=frac[i-1]*i%mod;
ifrac[sz]=inv(frac[sz]);
for(int i=sz;i>=1;--i) ifrac[i-1]=ifrac[i]*i%mod;
for(int i=0;i<=7;++i) cin>>a[i];
for(int i=0;i<=7;++i) cin>>b[i];
int x=ceil(a[0]/10.0),y=ceil(b[0]/10.0),ans=0;
for(int i=y;i<=x+y-1;++i){
ans=(ans+C(i-1,y-1)*inv(fpow(2,i))%mod)%mod;
}
cout<<ans<<"\n";
return 0;
}
I-Yet Another FFT Problem?
我们需要找到一对 a i + b l = a j + b k a_i+b_l=a_j+b_k ai+bl=aj+bk,那么只需暴力记录它们的和,然后在时限范围内必然会发生碰撞。预处理的时候记得去重(如果两个序列都有重复,显然成立),减少枚举次数。
#include<bits/stdc++.h>
#define mk make_pair
#define pii pair<int,int>
#define F first
#define S second
using namespace std;
const int N=1e6+2;
int n,m,a[N],b[N];
int cnt[10*N];
pii cya,cyb,O,mp[20*N];
vector<int>va,vb;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=m;++i) cin>>b[i];
for(int i=1;i<=n;++i){
if(cnt[a[i]]){
cya={cnt[a[i]],i};
}else va.emplace_back(i);
cnt[a[i]]=i;
}
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=m;++i){
if(cnt[b[i]]){
cyb={cnt[b[i]],i};
}else vb.emplace_back(i);
cnt[b[i]]=i;
}
if(cya!=O&&cyb!=O){
cout<<cya.F<<" "<<cya.S<<" "<<cyb.F<<" "<<cyb.S<<"\n";
return 0;
}
a[0]=b[0]=-1;
for(auto i:va){
for(auto j:vb){
int sum=a[i]+b[j];
if(mp[sum]!=O){
cout<<mp[sum].F<<" "<<i<<" "<<mp[sum].S<<" "<<j<<"\n";
return 0;
}else mp[sum]=mk(i,j);
}
}
cout<<-1<<"\n";
return 0;
}