[IOI2018]werewolf狼人——kruskal重構樹+可持久化線段樹
阿新 • • 發佈:2018-10-08
lse style 都是 pda 位置 roo sca com font
題目鏈接:
IOI2018werewolf
題目中編號都是從0開始,太不舒服了,我們按編號從1開始講QAQ。
題目大意就是詢問每次從一個點開始走只能走編號在[l,n]中的點,在任意點變成狼,之後只能走[0,r]中的點,是否能到達另一個點。
後一部分其實就是找有哪些點只走[0,r]中的點能到達終點,那麽反過來看,就是終點只走[0,r]中的點能到達哪些點。
那麽只要起點能到達的點和終點能到達的點中有交集就有解。
因為起點只能走一些編號較大的點,那麽我們求出原圖的最大生成樹,建出kruskal重構樹,求出重構樹的dfs序,每次詢問在重構樹上倍增找能到達的聯通塊在dfs序上的區間就好了。
相反,從終點走就求出原圖的最小生成樹,然後也按上述方法找。
找到兩段dfs序區間後只要這兩段區間中有相同點就能判有解。
我們將每個點在第一個dfs序中的位置作為橫坐標,在第二個dfs序中的位置作為縱坐標,剩下的就是一個簡單的二維數點問題了。
等會,上面是不是差點什麽?原圖沒有邊權啊?
我們以最大生成樹為例,對於一條邊(x,y),兩個點能否通過這條邊互相到達,由這兩個點中較小的點決定,因此求最大生成樹時每條邊邊權就是邊兩端點中較小的那個。最小生成樹邊權就是兩端點中較大的那個。
整體思路就是分別建出原圖最小生成樹和最大生成樹的重構樹,分別求出每個點在兩個重構樹中的dfs序位置,然後可持久化線段樹二維數點。
kruskal重構樹在這裏就不贅述了,如果不是太了解可以參考我的另一篇博客NOI2018歸程 ,那道題和這道題思想很像。
題面似乎沒說保證整張圖聯通,因此可能是kruskal重構森林。
#include<map> #include<set> #include<stack> #include<queue> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct miku { int u,v,x,y; }a[400010]; int cnt; int n,m,k; int s,t,l,r; int num1,num2; int sum1,sum2; int s1[400010]; int s2[400010]; int t1[400010]; int t2[400010]; int q1[400010]; int q2[400010]; int v1[400010]; int v2[400010]; int fa1[400010]; int fa2[400010]; int ls1[400010]; int rs1[400010]; int ls2[400010]; int rs2[400010]; int ls[5000010]; int rs[5000010]; int vis1[400010]; int vis2[400010]; int sum[5000010]; int root[200010]; int f1[400010][18]; int f2[400010][18]; int find1(int x) { if(fa1[x]==x) { return x; } return fa1[x]=find1(fa1[x]); } int find2(int x) { if(fa2[x]==x) { return x; } return fa2[x]=find2(fa2[x]); } bool cmp1(miku a,miku b) { return a.x>b.x; } bool cmp2(miku a,miku b) { return a.y<b.y; } void build(int &rt,int l,int r) { rt=++cnt; if(l==r) { return ; } int mid=(l+r)>>1; build(ls[rt],l,mid); build(rs[rt],mid+1,r); } void updata(int &rt,int pre,int l,int r,int k) { rt=++cnt; sum[rt]=sum[pre]+1; if(l==r) { return; } ls[rt]=ls[pre]; rs[rt]=rs[pre]; int mid=(l+r)>>1; if(k<=mid) { updata(ls[rt],ls[pre],l,mid,k); } else { updata(rs[rt],rs[pre],mid+1,r,k); } } int query(int x,int y,int l,int r,int L,int R) { if(L<=l&&r<=R) { return sum[y]-sum[x]; } int mid=(l+r)>>1; int res=0; if(L<=mid) { res+=query(ls[x],ls[y],l,mid,L,R); } if(R>mid) { res+=query(rs[x],rs[y],mid+1,r,L,R); } return res; } void dfs1(int x) { vis1[x]=1; s1[x]=sum1; if(x<=n) { q1[++sum1]=x; } for(int i=1;i<=17;i++) { f1[x][i]=f1[f1[x][i-1]][i-1]; } if(ls1[x]) { dfs1(ls1[x]); } if(rs1[x]) { dfs1(rs1[x]); } t1[x]=sum1; } void dfs2(int x) { vis2[x]=1; s2[x]=sum2; if(x<=n) { q2[++sum2]=x; } for(int i=1;i<=17;i++) { f2[x][i]=f2[f2[x][i-1]][i-1]; } if(ls2[x]) { dfs2(ls2[x]); } if(rs2[x]) { dfs2(rs2[x]); } t2[x]=sum2; } int ST1(int x,int val) { for(int i=17;i>=0;i--) { if(v1[f1[x][i]]>=val&&f1[x][i]!=0) { x=f1[x][i]; } } return x; } int ST2(int x,int val) { for(int i=17;i>=0;i--) { if(v2[f2[x][i]]<=val&&f2[x][i]!=0) { x=f2[x][i]; } } return x; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { scanf("%d%d",&a[i].u,&a[i].v); a[i].u++; a[i].v++; a[i].x=min(a[i].u,a[i].v); a[i].y=max(a[i].u,a[i].v); } for(int i=1;i<2*n;i++) { fa1[i]=i; fa2[i]=i; } num1=num2=n; sort(a+1,a+1+m,cmp1); for(int i=1;i<=m;i++) { int fx=find1(a[i].u); int fy=find1(a[i].v); if(fx!=fy) { num1++; v1[num1]=a[i].x; ls1[num1]=fx; rs1[num1]=fy; f1[fx][0]=num1; f1[fy][0]=num1; fa1[fx]=num1; fa1[fy]=num1; if(num1==2*n-1) { break; } } } for(int i=1;i<=n;i++) { if(!vis1[i]) { dfs1(find1(i)); } } sort(a+1,a+1+m,cmp2); for(int i=1;i<=m;i++) { int fx=find2(a[i].u); int fy=find2(a[i].v); if(fx!=fy) { num2++; v2[num2]=a[i].y; ls2[num2]=fx; rs2[num2]=fy; f2[fx][0]=num2; f2[fy][0]=num2; fa2[fx]=num2; fa2[fy]=num2; if(num2==2*n-1) { break; } } } for(int i=1;i<=n;i++) { if(!vis2[i]) { dfs2(find2(i)); } } build(root[0],1,n); for(int i=1;i<=n;i++) { updata(root[i],root[i-1],1,n,s2[q1[i]]+1); } while(k--) { scanf("%d%d%d%d",&s,&t,&l,&r); s++; t++; l++; r++; s=ST1(s,l); t=ST2(t,r); int ans=query(root[s1[s]],root[t1[s]],1,n,s2[t]+1,t2[t]); if(ans==0) { printf("0\n"); } else { printf("1\n"); } } }
[IOI2018]werewolf狼人——kruskal重構樹+可持久化線段樹