洛谷P3603 || bzoj 4763 雪辉

https://www.luogu.org/problemnew/show/P3603

https://www.lydsy.com/JudgeOnline/problem.php?id=4763

就是先树分块,取所有的块顶为关键点

那么任意点与它祖先中离它最近的关键点的距离为根号级别

先预处理出任意两个关键点之间的路径上有的值的bitset

(vv[i][j][k]表示i到j的路径上是否出现值k)

那么,只需要在询问时,对于路径(a,b),得到它路径上有的值的bitset;对于每个询问,把所有得到的bitset或一下就行了

得到bitset的方法见代码。。。

以下代码常数超大!为了卡常特意手写了bitset,还把块大小改到550(不是根号,实测这样更快...)

貌似有别的方法重建树的,常数小得多了

  1 #pragma GCC optimize(3)
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<vector>
  6 #include<bitset>
  7 using namespace std;
  8 #define fi first
  9 #define se second
 10 #define mp make_pair
 11 #define pb push_back
 12 typedef long long ll;
 13 typedef unsigned long long ull;
 14 typedef pair<int,int> pii;
 15 int n,m,F;
 16 vector<int> e[100100];
 17 int dd[100100];
 18 const int bsz=480;
 19 ull lft[64];
 20 void init()
 21 {
 22     lft[0]=1;
 23     for(int i=1;i<64;i++)    lft[i]=lft[i-1]<<1;
 24 }
 25 struct bs
 26 {
 27 ull d[bsz];
 28 int _Find_first_zero()
 29 {
 30     for(int i=0;i<bsz;i++)
 31         if(d[i]!=0xffffffffffffffff)
 32             return (i<<6)+__builtin_ffsll(~d[i])-1;
 33 }
 34 void reset()
 35 {
 36     for(int i=0;i<bsz;i++)    d[i]=0;
 37 }
 38 void set(int p,bool q=1)
 39 {
 40     if(q)    d[p>>6]|=lft[p-((p>>6)<<6)];
 41     else    d[p>>6]&=~lft[p-((p>>6)<<6)];
 42 }
 43 int count()
 44 {
 45     int ans=0;
 46     for(int i=0;i<bsz;i++)    ans+=__builtin_popcountll(d[i]);
 47     return ans;
 48 }
 49 bs &operator|=(const bs &b)
 50 {
 51     for(int i=0;i<bsz;i++)    d[i]|=b.d[i];
 52     return *this;
 53 }
 54 };
 55 int bl[100100],rt[310],cnt;
 56 const int sz=550;
 57 namespace GBLOCK
 58 {
 59 int st[100100],tp;
 60 void dfs(int u,int fa)
 61 {
 62     int i,ltp=tp;
 63     for(i=0;i<e[u].size();i++)
 64         if(e[u][i]!=fa)
 65         {
 66             dfs(e[u][i],u);
 67             if(tp-ltp>=sz)
 68             {
 69                 rt[++cnt]=u;
 70                 while(tp!=ltp)    bl[st[tp--]]=cnt;
 71             }
 72         }
 73     st[++tp]=u;
 74 }
 75 void work()
 76 {
 77     dfs(1,0);
 78     ++cnt;rt[cnt]=1;
 79     while(tp)    bl[st[tp--]]=cnt;
 80 }
 81 }
 82 namespace LCA
 83 {
 84 int anc[100100][20],dep[100100],l2n=19;
 85 void dfs(int u,int fa)
 86 {
 87     int i;
 88     anc[u][0]=fa;
 89     dep[u]=dep[fa]+1;
 90     for(i=1;i<=l2n;i++)
 91         anc[u][i]=anc[anc[u][i-1]][i-1];
 92     for(i=0;i<e[u].size();i++)
 93         if(e[u][i]!=fa)
 94             dfs(e[u][i],u);
 95 }
 96 int lca(int x,int y)
 97 {
 98     int t,i;
 99     if(dep[x]<dep[y]){t=x;x=y;y=t;}
100     for(t=dep[x]-dep[y],i=0;t>0;t>>=1,i++)
101         if(t&1)    x=anc[x][i];
102     if(x==y)    return x;
103     for(i=l2n;i>=0;i--)
104         if(anc[x][i]!=anc[y][i])
105         {
106             x=anc[x][i];
107             y=anc[y][i];
108         }
109     return anc[x][0];
110 }
111 }
112 using LCA::lca;
113 using LCA::dep;
114 using LCA::anc;
115 int nn[100100],nnn;
116 bs vv[310][310];//vv[i][j]:rt[i],rt[j]间答案
117 int num[30100];bs vis;
118 namespace PRE
119 {
120 int s;
121 void dfs(int u,int fa)
122 {
123     int i;
124     if(!num[dd[u]])    vis.set(dd[u]);
125     ++num[dd[u]];
126     if(nn[u])    vv[s][nn[u]]=vis;
127     for(i=0;i<e[u].size();i++)
128         if(e[u][i]!=fa)
129             dfs(e[u][i],u);
130     --num[dd[u]];
131     if(!num[dd[u]])    vis.set(dd[u],0);
132 }
133 void work()
134 {
135     int i;
136     for(i=1;i<=cnt;i++)
137         if(!nn[rt[i]])
138             nn[rt[i]]=++nnn;
139     for(i=1;i<=n;i++)
140         if(nn[i])
141         {
142             s=nn[i];
143             dfs(i,0);
144         }
145 }
146 }
147 bs tt;
148 void jump1(int &x,int ed)
149 {
150     if(ed==-1)    ed=rt[bl[x]];
151     while(x!=ed)
152     {
153         tt.set(dd[x]);
154         x=anc[x][0];
155     }
156     tt.set(dd[x]);
157 }
158 void jump2(int &x,int ed)
159 {
160     int x1=x;
161     while(x1!=1&&dep[rt[bl[x1]]]>=dep[ed])    x1=rt[bl[x1]];
162     tt|=vv[nn[x]][nn[x1]];
163     x=x1;
164 }
165 int main()
166 {
167 
168     init();
169     int i,a,b,a1,a2,t,lans=0,l;
170     scanf("%d%d%d",&n,&m,&F);
171     for(i=1;i<=n;i++)    scanf("%d",&dd[i]);
172     for(i=1;i<n;i++)
173     {
174         scanf("%d%d",&a,&b);
175         e[a].pb(b);e[b].pb(a);
176     }
177     GBLOCK::work();LCA::dfs(1,0);PRE::work();
178     while(m--)
179     {
180         scanf("%d",&t);tt.reset();
181         for(i=1;i<=t;i++)
182         {
183             scanf("%d%d",&a,&b);
184             if(F)    a^=lans,b^=lans;
185             l=lca(a,b);
186             if(a!=1&&dep[rt[bl[a]]]>=dep[l])    jump1(a,-1);
187             if(b!=1&&dep[rt[bl[b]]]>=dep[l])    jump1(b,-1);
188             jump2(a,l);
189             jump2(b,l);
190             jump1(a,l);jump1(b,l);
191         }
192         a1=tt.count();
193         a2=tt._Find_first_zero();
194         printf("%d %d\n",a1,a2);
195         lans=a1+a2;
196     }
197     return 0;
198 }

此方法相比直接分块然后处理边角,常数还是算小的,能够比bzoj2589https://www.cnblogs.com/hehe54321/p/9463519.html里面的做法快,但是还是过不去...


猜你喜欢

转载自www.cnblogs.com/hehe54321/p/9489906.html