【NOIP模拟赛三】并查集+hash day2 third 雪后村庄(好题)

来源:互联网 发布:java摇奖机转盘 编辑:程序博客网 时间:2024/04/29 18:59

题目描述

 

输入

输出

输出q行,每行一个字符串“yes”或“no”(不包括引号)。

样例输入

(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

2 43 41 2 32 3 22 4 41 2 31 3 22 3 23 4 441 3 31 3 21 4 33 4 4

样例输出

noyesnono

提示


    其实我真正想写的是这道题啦……

    乍一看,求瓶颈边!只要判断每个村庄中a、b瓶颈边是否小于等于k就可以了,所以使用Kruskal树来做!

    但是询问和村庄数都很多,这使得q次就是q*n,无法跑过1s(废话)

    那么怎么办呢?注意到其实题目并不是让我们求瓶颈边,而是判断瓶颈边是否小于等于k,或者这样说:判断只使用小于等于k的边能否使a和b连通。

    我们考虑离线算法,将询问当成边一起做。使用并查集维护连通性,遇到询问时只需要判断每个村庄的a、b是否在同一并查集即可。但是这样仍然是q*n,会TLE(废话,要判断n次)。

    我们考虑利用启发式合并暴力合并并查集,让每个点直接指向根节点(其实就是集合标号),合并时让节点少的标号修改成节点多的,平均复杂度便是log(n*m)。用一个hash表记录每个节点在不同村庄的集合,判断时只需要判断a和b的hash值是否相等即可。最后优化一下常数、写一写读入优化神马的,可以跑进1s。

    代码如下:

#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define mod 1000000007 #define mod2 998244353 #define N 200005 #define M 500005 int m,n,p,q,line[N]; int Set[N],sum[N];//属于哪个集合、集合中的元素个数 int hash[N];//每个元素的哈希值 int f[N],f2[N],fir[N],nex[N]; int ans[N]; void read(int &p) {     p=0;     char c=getchar();     while(c<'0'||c>'9')c=getchar();     while(c>='0'&&c<='9')p=p*10+c-'0',c=getchar(); } struct node {     int s,t,l,w;     bool operator<(node b)const    {         return l==b.l?w<b.w:l>b.l;     } }A[M]; void upd(int a,int k)//将a号点标号变成k O(1) {     int w=(a-1)%m+1,p=(a-1)/m;     hash[w]=((hash[w]+1ll*(k-Set[a])*f[p]%mod2)%mod2+mod2)%mod2;     //hash2[w]=((hash2[w]+1ll*(k-Set[a])*f2[p]%mod)%mod+mod)%mod;     Set[a]=k; } bool cmp(node a,node b) {     if(!a.w)         return 0;     if(!b.w)         return 1;     return a.w<b.w; } int main() {    read(n);read(m);     for(int i=1;i<=n;i++)//200000         read(line[i]);     f[0]=1;     for(int i=1;i<=n;i++)//200000         f[i]=1ll*f[i-1]*7%mod2;     for(int i=1;i<=n;i++)//200000     {         for(int j=1;j<=line[i];j++)         {             read(A[p+j].s);read(A[p+j].t);read(A[p+j].l);             A[p+j].s+=(i-1)*m;             A[p+j].t+=(i-1)*m;         }         p+=line[i];     }     for(int i=1;i<=n*m;i++)//200000     {         upd(i,i);         sum[i]=1;         fir[i]=i;     }     read(q);     for(int i=1;i<=q;i++)     {         read(A[p+i].s);read(A[p+i].t);read(A[p+i].l);         A[p+i].w=i;     }     p+=q;     sort(A+1,A+p+1);     int x,y;     for(int i=1;i<=p;i++)     {         x=Set[A[i].s];         y=Set[A[i].t];         if(!A[i].w)         {             if(x!=y)             {                 if(sum[x]>sum[y])                     swap(x,y);                 int j;                 for(j=fir[x];;j=nex[j])                 {                     upd(j,y);                     if(!nex[j])                         break;                 }                 nex[j]=fir[y];                 fir[y]=fir[x];                 sum[y]+=sum[x];             }         }         else            ans[A[i].w]=hash[A[i].s]==hash[A[i].t];     }     for(int i=1;i<=q;i++)     {         if(ans[i])             printf("yes\n");         else            printf("no\n");     } } 


原创粉丝点击