Codeforces 1198 简要题解

本文详细介绍了Codeforces 1198比赛中的E题和F题的解题思路。E题采用扫描线策略将原图划分,并利用行列二分图匹配及差分构建图,通过dinic算法求解最小割。F题则通过随机化和DLZ常数剪枝方法实现解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

传送门

前四题对应这套题 C C C~ F F F

E

传送门
考虑用扫描线的思想将原图分成若干小矩形,然后就可以利用行列二分图匹配+差分的思想建图,最后用 d i n i c dinic dinic跑最小割即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef pair<int,int> pii;
const int N=505,inf=1e9;
int sx,sy,vx[N],vy[N],trans[N][N],n;
pii a[N];
struct edge{int v,c,next;};
namespace dinic{
	edge e[1000005];
	int first[N],cur[N],d[N],cnt,s,t;
	inline void init(){memset(first,-1,sizeof(first)),cnt=-1;}
	inline void add(int u,int v,int c){e[++cnt]=(edge){v,c,first[u]},first[u]=cnt;}
	inline void addedge(int u,int v,int c){add(u,v,c),add(v,u,0);}
	inline bool bfs(){
		static int q[N],hd,tl;
		for(ri i=s;i<=t;++i)cur[i]=d[i]=-1;
		d[q[hd=tl=1]=s]=0;
		while(hd<=tl){
			int x=q[hd++];
			cur[x]=first[x];
			for(ri v,i=first[x];~i;i=e[i].next){
				if(~d[v=e[i].v]||e[i].c<=0)continue;
				d[q[++tl]=v]=d[x]+1;
			}
		}
		return ~d[t];
	}
	inline int dfs(int x,int f){
		if(x==t||!f)return f;
		int flow=f,v,t;
		for(ri &i=cur[x];~i;i=e[i].next){
			if(!flow)return f;
			if(d[v=e[i].v]==d[x]+1&&e[i].c>0){
				t=dfs(v,min(flow,e[i].c));
				if(!t)d[v]=-1;
				flow-=t,e[i].c-=t,e[i^1].c+=t;
			}
		}
		return f-flow;
	}
	inline int solve(){
		int ret=0,tmp;
		while(bfs())while(tmp=dfs(s,inf))ret+=tmp;
		return ret;
	}
}
int main(){
	n=read(),n=read();
	dinic::init();
	for(ri i=1,up=n<<1;i<=up;++i)a[i].fi=vx[++sx]=read()-(i&1),a[i].se=vy[++sy]=read()-(i&1);
	sort(vx+1,vx+sx+1),sx=unique(vx+1,vx+sx+1)-vx-1;
	sort(vy+1,vy+sy+1),sy=unique(vy+1,vy+sy+1)-vy-1;
	for(ri i=1,up=n<<1;i<=up;++i){
		a[i].fi=lower_bound(vx+1,vx+sx+1,a[i].fi)-vx;
		a[i].se=lower_bound(vy+1,vy+sy+1,a[i].se)-vy;
	}
	dinic::t=sx+sy+1;
	for(ri i=1;i<sx;++i)dinic::addedge(dinic::s,i,vx[i+1]-vx[i]);
	for(ri i=1;i<sy;++i)dinic::addedge(i+sx,dinic::t,vy[i+1]-vy[i]);
	for(ri A=1,B=2,up=n<<1;A<=up;A+=2,B+=2)for(ri i=a[A].fi;i<a[B].fi;++i)for(ri j=a[A].se;j<a[B].se;++j)trans[i][j]=1;
	for(ri i=1;i<sx;++i)for(ri j=1;j<sy;++j)if(trans[i][j])dinic::addedge(i,j+sx,inf);
	cout<<dinic::solve();
	return 0;
}

F

传送门
直接随机化+ D L Z DLZ DLZ常数剪枝就能过啦!
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef pair<int,int> pii;
const int N=2e5+5;
int n,a[N];
pii vl[N];
namespace small{
	inline void Main(){
		for(ri g1=0,g2=0,s=1,up=1<<n;s<up;++s,g1=0,g2=0){
			for(ri i=1;i<=n;++i)
			if((s>>(i-1))&1)g1=__gcd(a[i],g1);
			else g2=__gcd(a[i],g2);
			if(g1==1&&g2==1){
				puts("YES");
				for(ri j=1;j<=n;++j)cout<<(((s>>(j-1))&1)+1)<<' ';
				exit(0);
			}
		}
		puts("NO");
		exit(0);
	}
}
inline bool check(){return clock()*1000/CLOCKS_PER_SEC<=450;}
namespace big{
	int id[N],res[N];
	inline void Main(){
		srand(time(NULL));
		for(ri i=1;i<=n;++i)vl[i]=pii(a[i],i);
		int g,t,gg;
		while(check()){
			random_shuffle(vl+1,vl+n+1);
			g=gg=0;
			for(ri i=1;i<=n;++i)id[i]=0;
			for(ri i=1;i<=n;++i){
				t=__gcd(g,vl[i].fi);
				if(t^g)id[i]=1;
				else gg=__gcd(gg,vl[i].fi);
				g=t;
				if(g^1)continue;
				for(ri j=i+1;j<=n;++j)gg=__gcd(gg,vl[j].fi);
				if(gg==1){
					puts("YES");
					for(ri j=1;j<=n;++j)res[vl[j].se]=id[j]+1;
					for(ri j=1;j<=n;++j)cout<<res[j]<<' ';
					puts("");
					exit(0);
				}
				break;
			}
			
		}
		puts("NO");
		exit(0);
	}
}
int main(){
	n=read();
	int g=0;
	for(ri i=1;i<=n;++i)g=__gcd(g,a[i]=read());
	if(g^1)return puts("NO"),0;
	if(n<=20)small::Main();
	big::Main();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值