BZOJ5361[Lydsy1805月赛]对称数——主席树+随机化
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5361
好神的一道题啊!
容易看出来是要用维护权值的数据结构,因此树链剖分首先pass掉。
那么不妨用树上主席树试试?每个版本存当前点到根路径上的点的权值。
如果维护区间权值数量的话,你发现没有明确的判断条件来明确每一次主席树上二分是走左子树还是右子树。
这时就要用到一个套路了:将1~200000的所有权值随机映射成unsigned long long的数,主席树维护区间权值异或和。
再维护前缀权值异或和,这样每次在主席树上二分时只要判断左子树的权值异或和是否等于左子树代表的区间的权值异或和。
如果等于,就说明左子树所有权值都出现了奇数次,答案一定在右子树中。反之则在左子树中。
因为是随机化算法,所以只能保证大概率的正确性,不过这种套路在做题时也可以适当借鉴。
最后注意主席树区间要开到200001,因为可能前200000个数都有,答案是200001。
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ull unsigned long long
using namespace std;
inline char _read()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
int x=0,f=1;char ch=_read();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}
return x*f;
}
int T;
int x,y;
int n,m;
int cnt;
int tot;
int a[200010];
int d[200010];
ull v[200010];
int to[400010];
int ls[8000010];
int rs[8000010];
ull num[200010];
int root[200010];
int head[200010];
int next[400010];
ull sum[8000010];
int f[200010][19];
ull Rand()
{
return ((ull)rand()<<45)|((ull)rand()<<30)|(rand()<<15)|rand();
}
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void updata(int &rt,int pre,int l,int r,int k)
{
rt=++cnt;
if(l==r)
{
sum[rt]=sum[pre]^v[l];
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);
}
sum[rt]=sum[ls[rt]]^sum[rs[rt]];
}
void dfs(int x)
{
d[x]=d[f[x][0]]+1;
updata(root[x],root[f[x][0]],1,200001,a[x]);
for(int i=1;i<=18;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x][0])
{
f[to[i]][0]=x;
dfs(to[i]);
}
}
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=18;i++)
{
if((dep&(1<<i))!=0)
{
x=f[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=18;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int query(int x,int y,int fa,int anc,int l,int r)
{
if(l==r)
{
return l;
}
ull res=sum[ls[x]]^sum[ls[y]]^sum[ls[fa]]^sum[ls[anc]];
int mid=(l+r)>>1;
if(res==(num[mid]^num[l-1]))
{
return query(rs[x],rs[y],rs[fa],rs[anc],mid+1,r);
}
else
{
return query(ls[x],ls[y],ls[fa],ls[anc],l,mid);
}
}
int main()
{
srand(20020419);
T=read();
for(int i=1;i<=200001;i++)
{
v[i]=Rand();
num[i]=num[i-1]^v[i];
}
while(T--)
{
cnt=0;
tot=0;
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
memset(sum,0,sizeof(sum));
memset(root,0,sizeof(root));
memset(head,0,sizeof(head));
n=read();
m=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
}
for(int i=1;i<n;i++)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(1);
while(m--)
{
x=read();
y=read();
int anc=lca(x,y);
printf("%d\n",query(root[x],root[y],root[anc],root[f[anc][0]],1,200001));
}
}
}
BZOJ5361[Lydsy1805月赛]对称数——主席树+随机化的更多相关文章
- 【主席树上二分】bzoj5361: [Lydsy1805月赛]对称数
随机化选讲例题 题目大意 小 Q 认为,偶数具有对称美,而奇数则没有.给定一棵 n 个点的树,任意两点之间有且仅有一条直接或间接路径.这些点编号依次为 1 到 n,其中编号为 i 的点上有一个正整数 ...
- [BZOJ5361][Lydsy1805月赛]对称数
bzoj Description 给你一棵树,每个点有一个编号\(a_i\).\(Q\)组询问,每次问一条路径上最小的出现了偶数次的编号是多少(包括零次). 多组数据,\(T\le10,n,Q,a_i ...
- [Lydsy1805月赛]对称数 BZOJ5361
分析: 这个题,还是蛮有趣的.考虑,如果l,r区间内的所有数出现奇数次,那么[l-1,r]的抑或和等于所得抑或和. 之后怎么维护呢,主席树维护区间抑或和,记得将每个点附上一个ull级别的随机数,之后抑 ...
- [Lydsy1805月赛] 对称数
挺不错的一道数据结构题QWQ. 一开始发现这个题如果不看数据范围的话,妥妥的树上莫队啊23333,然鹅10组数据是不可能让你舒舒服服的树上莫队卡过的23333 于是想了想,这个题的模型就是,把u到v链 ...
- BZOJ 4408: [Fjoi 2016]神秘数 [主席树]
传送门 题意: 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},8无法表示为集合S的子集的和,故集合S的神秘数为8.现给定n个正整数a[1]. ...
- BZOJ4408&4299[Fjoi 2016]神秘数——主席树
题目描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 1 2 = 1+1 3 = 1+1+1 4 = 4 5 = 4+1 6 = ...
- 【bzoj4408】[Fjoi 2016]神秘数 主席树
题目描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1+1+14 = 45 = 4+16 = 4+1+1 ...
- P4587 [FJOI2016]神秘数(主席树)
题意:给出1e5个数 查询l,r区间内第一个不能被表示的数 比如1,2,4可以用子集的和表示出[1,7] 所以第一个不能被表示的是8 题解:先考虑暴力的做法 把这个区间内的数字按从小到大排序后 从前往 ...
- COGS 930. [河南省队2012] 找第k小的数 主席树
主席树裸板子 #include<cstdio> #include<iostream> #include<algorithm> #define MAXN 100005 ...
随机推荐
- Android Studio中依赖第三库导致support版本冲突解决方案
1.今天在Android Studio的app/gradle文件中依赖文件选择器的第三方库:“com.leon:lfilepickerlibrary:1.8.0” 时,github地址:https:/ ...
- abp 取消权限校验
在abp中,通过ABP_PERMISSIONS表来存储定义appService中的方法权限校验.设置方式如下: [AbpAuthorize(PermissionNames.Pages_Users)] ...
- 复习C#的方法Math.Max和Math.Min
温故而知新,今天学习Math.Max和Min的方法.这2个方法,均需要传入2个参数,返回参数中最大值和最小值. class Ac { public void LeanMathFunction() { ...
- 【持续更新中···】Linux下的小技巧
1.Linux回到上级文件的命令: cd ..回到上一级目录(注意:cd 和..中间有空格) cd ~回到home目录 cd -回到某一目录
- Redis_简单使用
可基于内存也可持久化的Key-Value(字典, Remote Dictionary Server,远程字典服务器)数据库. 客户端:http://redis.io/clients 命令:http:/ ...
- 网页录像录音功能的实现之MediaRecorder的使用
前面介绍了通过H5实现在网页内打开摄像头和麦克风,实现截图和图像预览的相关知识. getUserMedia API及HTML5 调用摄像头和麦克风 一个简单的demo 实现摄像头的调用及视频录制,截 ...
- D. Cutting Out
---恢复内容开始--- 链接 [https://codeforces.com/contest/1077/problem/D] 题意 给你n,k,n个数,找出长度为k,的子串(不需连续),使得该子串数 ...
- Linux内核设计与实现 第三章
1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作都是由内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程不过是一种特殊的 ...
- spatial-temporal information extraction典型方法总结
==================================== 咳咳咳 由于科研的直接对象就是video sequence,所以,如何更好地提取spatial-temporal inform ...
- Github上传更新
通过2天的时间,不停的网上找各种资料,今天下午终于可以登录上github for Windows 客户端了,,, 然后通过一整晚的摸索,也把项目上传到github里. github地址:https:/ ...