Jzoj5446 高考是不可能高考的

Snuke 喜欢旗子.
Snuke 正在将N 个旗子摆在一条线上.
第i 个旗子可以被放在位置xi 或yi 上.

Snuke 认为两个旗子间的最小距离越大越好. 请你求出最大值.


今天这都是些集训队作业啊!

这个题本来以为一眼能做结果死磕磕不动

二分答案肯定是要的,问题是怎么判定解

1.dp肯定不行了

2.数据结构,也不行

3.图的最大独立集,比较靠谱但是。。。复杂度爆炸

正解:2SAT

考完听他们说才恍然大悟,我在分析的时候漏掉了一个条件,重点都放在了"xi,yi只能选一个"

但是还有一个条件:“xi,yi必须选一个”,我把这个重要条件作为判断二分可行性最后才用了

说说怎么构图

我们将所有的xi,yi放入一个数组s并排序,我们假设ai=0/1表示si这个位置有没有放旗子

显然有两种边:

1.若si和sj本来是同一个同一个k的xk,yk的话,这两者只可选择其一,即ai^aj=1

2.|si-sj|<我们二分的Mid,那么这两者最多选一个即ai&aj=0

很明显,如果直接暴力构图是过不去的,我们可以考虑以下两种优化方法:

1.线段树优化连边

2.分块优化连边

具体思路就是建立一些辅助点,当一个点x向一片区间s[l,r]连边的时候不需要每个都连接而是通过辅助点,这样可以减少边的总数(类似于中转站)

当然以上两种做法都比较复杂,我讲一种很简单而且很快的做法

我们二分答案的时候,我们发现需要将r开得很大(10^9)

而且我们注意到,如果暴力连边,Mid越大,边的数量就越多,而答案通常不大

所以我们考虑尽量减少r来降低复杂度

这里我们考虑一种dp的形式

假设这个问题中,xi,yi可以同时取,那么这个问题就变成了一个在s上的动态规划

方程为f[j]=max(f[i]+1){s[i]<=s[j]-Mid)

那么显然,这样做出来的答案会比正确答案更优(但是不正确),但是不会脱离太远(平均情况下<=10Answer)

所以我们将其作为一个二分的上界不会出问题只会优化时间复杂度

让后就莫名其妙拿了rank5[>_<]!

#include
#include
#include
#include
#define N 10010
using namespace std;
struct Graph{
	vector<int> G[N<<2];
	int dfn[N<<2],low[N<<2],stk[N<<2],top;
	int cnt,col[N<<2],clk,Col;
	inline void clear(int m){ 
		cnt=top=clk=Col=0; 
		for(int i=1;i<=m;++i) G[i].clear();
		memset(dfn,0,sizeof dfn);
		memset(col,0,sizeof col);
	}
	inline void adj(int x,int y){ G[x].push_back(y); }
	void dfs(int x){
		dfn[x]=low[x]=++clk;
		stk[++top]=x;
		for(int v,i=0,z=G[x].size();iif(!dfn[v=G[x][i]]){
				dfs(v); low[x]=min(low[x],low[v]);
			} else if(!col[v]) low[x]=min(dfn[v],low[x]);
		if(low[x]==dfn[x]){
			++Col;
			do{ col[stk[top]]=Col; } while(stk[top--]!=x);
		}
	}
	bool gScn(int n){
		for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i);
		n>>=1;
		for(int i=1;i<=n;++i) if(col[i]==col[i+n]) return 0;
		return 1;
	}
} G;
struct dt{ int v,r; } s[N<<1];
int x[N],y[N],p[N<<1],n,m=0,c[N];
inline bool c1(dt a,dt b){ return a.vinline int lowerbound(int p,int k){
	int l=1,r=p;
	for(int M;l>1;
		if(s[M].v<=k) l=M+1;
		else r=M;
	}
	return l;
}
inline int upperbound(int p,int k){
	int l=p,r=m;
	for(int M;l1>>1;
		if(s[M].v>=k) r=M-1;
		else l=M;
	}
	return l;
}
void buildGraph(int x){
	G.clear(m<<1);
	for(int i=1;i<=m;++i) G.adj(i,p[i]+m),G.adj(i+m,p[i]);
	for(int l,r,i=1;i<=m;++i){
		l=lowerbound(i,s[i].v-x);
		r=upperbound(i,s[i].v+x);
		for(;lfor(;r>i;--r) G.adj(i,r+m);
	}
}
int v[N<<1],f[N<<1];
inline bool ok(int x){
	v[0]=-100000000;
	memset(f,0,sizeof f); f[0]=0;
	for(int i=1,j;i<=m;++i){
		j=lower_bound(v,v+1+m,v[i]-x)-v;
		while(v[j]>v[i]-x) --j;
		f[i]=max(f[i-1],f[j]+1);
	}
	return f[m]>=n;
}
int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d",x+i,y+i);
		s[++m]=(dt){x[i],i};
		s[++m]=(dt){y[i],i};
	}
	sort(s+1,s+1+m,c1);
	for(int i=1;i<=m;++i){
		v[i]=s[i].v;
		if(!c[s[i].r]) c[s[i].r]=i;
		else { p[c[s[i].r]]=i; p[i]=c[s[i].r]; }
	}
	int l=0,r=100000000;
	for(int M;l1>>1;
		if(ok(M)) l=M;
		else r=M-1;
	}
	l=0;
	for(int M;l1>>1;
		buildGraph(M);
		if(G.gScn(m<<1)) l=M;
		else r=M-1;
	}
	printf("%d\n",l);
}

你可能感兴趣的:(OI,图论,----路径,--------连通性,求解策略,----动态规划,----二分/三分,--------序列形dp,Jzoj)