Time Limit: 39 Sec Memory Limit: 666 MB
Submit: 151 Solved: 80
Description
上次立下的NOIP退役Flag没有成功
这次就立一个WC狗牌的Flag
三周目的由乃被钦定成为了卡密,她立刻赶去二周目的世界寻找雪辉
但是按照设定,两个平行世界是没法互相影响的,也就是原则上由乃是没法去二周目世界的
这时候Deus又跳出来说,其实设定是作者骗你的,只要爱的力量足够强大什么都可以做到(好狗血)
Deus:由乃你为了雪辉是不是什么都可以做呀
yuno:当然啦这还用想
Deus:那你帮我做个题吧
yuno:只要不是数据结构,什么题我都做
Deus:出题人是那个nzhtl1477呀,他出(抄)的题除了傻逼数据结构还有啥。。。
yuno:你说的很有道理。。。
Deus:上次那个题你不是两分钟就秒了吗,这个题比那个还简单
yuno:(小声)其实那个是bzoj上面的大佬帮我做的
Deus:好吧就这么愉快的钦定了
给一个n个点的树,点有点权,有m次询问,每次询问多条链的并有多少种不同的点权以及它的mex
mex就是一个集合中最小的没有出现的非负整数,注意0要算
比如说集合是1,9,2,6,0,8,1,7,则出现了0,1,2,6,7,8,9这7种不同的点权,因为没有3所以mex是3
Input
第一行三个数n,m,意义如题所述,和一个数f
如果f是0,代表Deus没有使用膜法,如果f是1,代表Deus使用了膜法
之后一行n个数,表示点权
之后n-1行,每行两个数x,y,表示x和y节点之间有一条边,保证是一个树
之后m行,每行先是一个数a,表示这次输入a条链,紧接着2a个数(x1,y1)(x2,y2)...表示每条树链
如果数据被Deus施了膜法,这2a个数都要异或上上一个询问的答案lastans,如果是第一次询问则这个lastans = 0,因为每次询问有两个答案,lastans为这两个答案的和
如果没有膜法,则-1s并且不异或
数据范围:
设a的和为q
对于20%的数据,n,q<=1000,f=0
对于另外30%的数据,n,q<=100000,树是一条链,f=0
对于所有数据n,q<=100000,且点权<=30000
Output
m行,每行两个数表示点权种类数以及mex
Sample Input
10 1 1
0 0 1 0 0 2 2 0 0 0
2 3
1 2
4 5
3 4
7 8
6 7
5 6
9 10
8 9
4
1 7
3 3
1 1
9 3
0 0 1 0 0 2 2 0 0 0
2 3
1 2
4 5
3 4
7 8
6 7
5 6
9 10
8 9
4
1 7
3 3
1 1
9 3
Sample Output
3 3
HINT
可爱(口径)即正义~
Source
树 树分块 bitset
询问区间mex,理论上桶是必须要开的。这题强制在线不能树上莫队,那就只能用bitset即时维护了。
直接爬树链显然不可取,我们可以考虑树分块。
统计一条链(x,y)的答案时,拆成(x,LCA)和(y,LCA)分别处理,先向上跳到当前点所在块的中心,然后整块往上跳,再跳完零碎的部分。
因为我们要查询的是链的信息,和区域无关,所以分块应该按照深度分而不是子树大小。
stl的bitset不支持询问mex,所以要手写bitset
博主码代码的时候出现了很多细节上的问题,导致复杂度不稳定,无情被卡。
这是一个悲伤的故事,这个故事告诉我们永远不要迷之自信地认为自己的诡异写法能艹过正解。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define UL long long
using namespace std;
const int mxn=;
const UL ful=0xFFFFFFFFFFFFFFFF;
int read(){
int x=,f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>='' && ch<=''){x=x*-''+ch;ch=getchar();}
return x*f;
}
void write(int x){
if(x>)write(x/);
putchar(''+x%);
return;
}
struct edge{
int v,nxt;
}e[mxn<<];
int hd[mxn],mct=;
void add_edge(int u,int v){
e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;return;
}
//
int num[];
struct BIT{
UL x[];
inline void clear(){memset(x,,sizeof x);return;}
void insert(int a){
int pos=a/+;
x[pos]|=1LL<<(a%);
return;
}
void operator |= (const BIT &b){
for(register int i=;i<;i++)x[i]|=b.x[i];
return;
}
int mex(){
for(int i=;i<;i++){
if(x[i]!=ful){
int res=;
UL tmp=x[i];
while(tmp&){
res++;
tmp>>=;
}
return res+(i-)*;
}
}
return *-;
}
int calc(){
int cnt=;
for(int i=;i<;i++){
if(!x[i])continue;
if(x[i]==ful)cnt+=;
else{
UL tmp=x[i];
cnt+=num[(tmp&)]+num[(tmp>>)&];
tmp>>=;
cnt+=num[(tmp&)]+num[(tmp>>)&];
}
}
return cnt;
}
}bt[][],res;
int block=;
int dep[mxn],mx[mxn],bct=;
int Cid[mxn],C[mxn],Cfa[mxn];
int fa[mxn][];
void DFS(int u,int ff){
mx[u]=;dep[u]=dep[ff]+;
for(int i=;i<=;i++)fa[u][i]=fa[fa[u][i-]][i-];
for(int i=hd[u];i;i=e[i].nxt){
int v=e[i].v;if(v==ff)continue;
fa[v][]=u;
DFS(v,u);
mx[u]=max(mx[u],mx[v]+);
}
if(mx[u]>=block || u==){
mx[u]=;
bct++; Cid[u]=bct; C[bct]=u; Cfa[bct]=;
}
return;
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=;i>=;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=;i>=;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
int n,Q,F;
int lastans=;
int w[mxn];
void Build(){
block=sqrt(n+0.5);
DFS(,);
for(int i=;i<=bct;i++){
int u=C[i];
res.clear();
res.insert(w[u]);u=fa[u][];
while(u){
res.insert(w[u]);
//
if(Cid[u]){
bt[i][Cid[u]]=res;
if(!Cfa[i])Cfa[i]=u;
}
//
u=fa[u][];
}
}
return;
}
void query(int x,int y){
if(dep[x]<dep[y])swap(x,y);
if(dep[x]-dep[y]<=block){
while(dep[x]>=dep[y]){
res.insert(w[x]);
x=fa[x][];
}
return;
}
while(dep[x]>=dep[y] && !Cid[x]){
res.insert(w[x]);
x=fa[x][];
}
int tmp=x;
while(dep[Cfa[Cid[tmp]]]>=dep[y]){
tmp=Cfa[Cid[tmp]];
}
if(tmp^x)res|=bt[Cid[x]][Cid[tmp]];
x=tmp;
while(dep[x]>=dep[y]){
res.insert(w[x]);
x=fa[x][];
}
return;
}
void solve(){
int a=read(),x,y;
res.clear();
while(a--){
x=read();y=read();
if(F){x^=lastans;y^=lastans;}
//
int tmp=LCA(x,y);
query(x,tmp);query(y,tmp);
//
}
int ans1=res.mex();
int ans2=res.calc();
// printf("%d %d\n",ans2,ans1);
write(ans2);putchar(' ');write(ans1);puts("");
lastans=ans1+ans2;//
return;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out2.txt","w",stdout);
int i,j,u,v;
n=read();Q=read();F=read();
for(i=;i<=n;i++)w[i]=read();
for(i=;i<n;i++){
u=read();v=read();
add_edge(u,v);
add_edge(v,u);
}
for(i=;i<=;i++){
int tmp=i;
while(tmp){
num[i]++;
tmp-=tmp&-tmp;
}
}
Build();
while(Q--)solve();
return ;
}