2022牛客暑期多校训练营10 个人题解

57 篇文章 0 订阅
7 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

RWLinno

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

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

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

打赏作者

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

抵扣说明:

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

余额充值