BZOJ3060: [Poi2012]Tour de Byteotia
Aug 18, 2015 07:57:09 PM
题解:
首先如果对于一条边的两个端点均满足$>k$,则显然可以缩到一起变成一个点.
然后对于剩下的边,我们需要添加若干条使得不出现环,因为如果出现了环肯定有$\leq{k}$的点在环上.
我们可以按任意顺序添加,因为最后图的形态是固定的.
所以直接并查集水过就可以.
代码(非常蠢):
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 1000010
#define M 2000010
struct Edge{
int a,b;
Edge(){}
Edge(int _a,int _b):a(_a),b(_b){
if(a>b)
swap(a,b);
}
bool operator<(const Edge&B)const{
return a>B.a;
}
}E[M];
inline int getc(){
static const int L=1<<15;
static char buf[L],*S=buf,*T=buf;
if(S==T){
T=(S=buf)+fread(buf,1,L,stdin);
if(S==T)
return EOF;
}
return*S++;
}
inline int getint(){
int c;
while(!isdigit(c=getc()));
int x=c-'0';
while(isdigit(c=getc()))
x=(x<<1)+(x<<3)+c-'0';
return x;
}
int r[N],siz[N];
inline int find(int x){
int q=x,rq;
for(;x!=r[x];x=r[x]);
for(;q!=x;rq=r[q],r[q]=x,q=rq);
return x;
}
inline void merge(int a,int b){
if(siz[a]>siz[b])
swap(a,b);
siz[b]+=siz[a];
r[a]=b;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("tt.in","r",stdin);
#endif
int n,m,k,i;
n=getint();
m=getint();
k=getint();
for(i=1;i<=m;++i)
E[i]=Edge(getint(),getint());
for(i=1;i<=n;++i)
r[i]=i,siz[i]=1;
int ra,rb,cnt=0;
for(i=1;i<=m;++i){
if(E[i].b<=k){
ra=find(E[i].a);
rb=find(E[i].b);
if(ra!=rb)
++cnt,merge(ra,rb);
}
}
for(i=1;i<=m;++i){
if(E[i].a>k){
ra=find(E[i].a);
rb=find(E[i].b);
++cnt;
if(ra!=rb)
merge(ra,rb);
}
}
for(i=1;i<=m;++i){
if(E[i].a<=k&&E[i].b>k){
ra=find(E[i].a);
rb=find(E[i].b);
if(ra!=rb)
++cnt,merge(ra,rb);
}
}
cout<<m-cnt<<endl;
return 0;
}
BZOJ1821:[JSOI2010]Group 部落划分 Group 二分+并查集
Jan 30, 2015 07:25:49 AM
思路:
我保证这是一种奇葩算法.
我们二分答案,那么问题就转化为若任意两个部落之间的距离\(\leq{l}\),是否能够划分为为\(k\)组.
我们能发现以下性质:若能够划分为\(k'\)组,且满足\(k'>k\),则必定能够划分为\(k\)组.原因是我们可以合并一些块,那么部落之间距离的最小值必然不会减少.
因此我们只需考虑对于一个给定的\(l\),最多能够划分为几组.
考虑两个点,若两点距离\(\leq{l}\),那么必定划分在一组中.那么现在有若干个连通块,不同连通块之间的所有点的距离均\(>l\),显然当前连通块数就是我们要求的最大连通块数.
于是二分+并查集水.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 1010
int n,k,x[N],y[N],r[N];
inline void init(){for(int i=1;i<=n;++i)r[i]=i;}
inline int find(int x){int q=x,rq;for(;x!=r[x];x=r[x]);for(;q!=x;q=rq)rq=r[q],r[q]=x;return x;}
inline void merge(int x,int y){r[find(x)]=find(y);}
typedef double f2;
f2 dis(int a,int b){
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
int main(){
scanf("%d%d",&n,&k);register int i,j;
for(i=1;i<=n;++i)scanf("%d%d",&x[i],&y[i]);
f2 Maxdis=0;
for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)Maxdis=max(Maxdis,dis(i,j));
f2 L=0,R=Maxdis,mid;
while(R-L>1e-4){
mid=(L+R)*.5;
init();
for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)if(dis(i,j)<=mid)merge(i,j);
int cnt=0;for(i=1;i<=n;++i)cnt+=(i==find(i));
if(cnt>=k)L=mid;else R=mid;
}
printf("%.2lf",L);
return 0;
}
BZOJ1396:识别子串 后缀自动机+并查集(&&BZOJ2865)
Jan 14, 2015 03:57:31 PM
思路:
我们建立原串的后缀树,那么发现出现次数为1的子串仅能出现在叶子节点上的父亲边上.(说起来很容易)
然后发现每一个更新可以分解为两端,每一段可以相当于一条斜率恒定的线段.
因此我们将两段分开,按照截距从小到大排序,并用并查集模拟覆盖即可.
(说起来很容易)
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
struct Node{
Node*tranc[26],*pa;int len,begin,siz,deg;
Node(){}
Node(int _len,int _begin):len(_len),begin(_begin){}
}Tnull,*null=&Tnull,mem[N<<1],*P=mem,*root,*last;
Node*Newnode(int _len,int _begin){
P->len=_len,P->begin=_begin;for(int i=0;i<26;++i)P->tranc[i]=null;P->pa=null;return P++;
}
char s[N];
struct Interval{
int l,r,k;
Interval(){}
Interval(int _l,int _r,int _k):l(_l),r(_r),k(_k){}
bool operator<(const Interval&B)const{return k<B.k;}
}S[N],S2[N];
int r[N];
inline void reset(int n){
for(register int i=1;i<=n+1;++i)r[i]=i;
}
inline int find(int x){
int q=x,rq;for(;x!=r[x];x=r[x]);for(;q!=x;q=rq)rq=r[q],r[q]=x;return x;
}
int v[N],vv[N];
Node*q[N<<1];int fr,ta;
int main(){
scanf("%s",s+1);int l=strlen(s+1);
register int i,j;int y;Node*p,*np,*q,*nq;
for(last=root=Newnode(0,0),i=l;i>=1;--i){
np=Newnode(last->len+1,i);np->siz=1;
for(p=last,y=s[i]-'a';p!=null&&p->tranc[y]==null;p=p->pa)p->tranc[y]=np;
if(p==null)np->pa=root;
else{
q=p->tranc[y];
if(q->len==p->len+1)np->pa=q;
else{
nq=Newnode(p->len+1,0);nq->pa=q->pa,q->pa=np->pa=nq;
memcpy(nq->tranc,q->tranc,sizeof q->tranc);
for(;p!=null&&p->tranc[y]==q;p=p->pa)p->tranc[y]=nq;
}
}last=np;
}
for(p=mem;p<P;++p)++p->pa->deg;
for(p=mem;p<P;++p)if(p->deg==0)::q[ta++]=p;
while(fr^ta){
p=::q[fr++];
if(p->pa!=null){
p->pa->siz+=p->siz;
if(!--p->pa->deg)
::q[ta++]=p->pa;
}
}
int id=0,id2=0;
for(p=mem;p<P;++p){
if(p->siz==1){
//printf("%d %d %d\n",p->begin,p->len,p->pa->len);
S[++id]=Interval(l-(p->len-p->pa->len)+1,l,1-p->begin);
S2[++id2]=Interval(p->begin,S[id].l-1,S[id].l-p->begin+1);
}
}sort(S+1,S+id+1),sort(S2+1,S2+id2+1);
reset(l);
memset(v,0x3f,sizeof v);
for(i=1;i<=id;++i){
for(j=find(S[i].l);j<=S[i].r;j=r[j]){
if(v[j]==0x3f3f3f3f)v[j]=S[i].k;
r[j]=find(j+1);
}
}
reset(l);
memset(vv,0x3f,sizeof vv);
for(i=1;i<=id2;++i){
for(j=find(S2[i].l);j<=S2[i].r;j=r[j]){
if(vv[j]==0x3f3f3f3f)vv[j]=S2[i].k;
r[j]=find(j+1);
}
}
for(i=1;i<=l;++i)printf("%d\n",min(l,min(v[i]+i,vv[i])));
return 0;
}