Jzoj5446 高考是不可能高考的
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<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#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();i<z;++i)
if(!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.v<b.v; }
inline int lowerbound(int p,int k){
int l=1,r=p;
for(int M;l<r;){
M=l+r>>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;l<r;){
M=l+r+1>>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(;l<i;++l) G.adj(i,l+m);
for(;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;l<r;){
M=l+r+1>>1;
if(ok(M)) l=M;
else r=M-1;
}
l=0;
for(int M;l<r;){
M=l+r+1>>1;
buildGraph(M);
if(G.gScn(m<<1)) l=M;
else r=M-1;
}
printf("%d\n",l);
}