[bzoj4928][SDOI省队集训2017]dierti
2017-05-31 20:34
183 查看
题目描述
对于一棵有根树,定义一个点u 的k− 子树为u 的子树中距离u 不超过k 的部分。注意,假如u 的子树中不存在距离u 为k 的点,则u 的k−子树是不存在的。定义两棵子树是相同的,当且仅当不考虑点的标号时,他们的形态是相同的(儿子的顺序也需要考虑)。给定一棵n 个点,点的标号在[1,n],以1 为根的有根树。问最大的k,使得存在两个点u ̸= v,满足u 的k− 子树与v 的k− 子树相同。
二分+hash
我们怎么对树hash?可以对括号序hash!二分答案,然后按照深度顺序加入点,线段树维护区间hash,就可以啦。
复杂度两个log,贼慢。
#include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) #define fd(i,a,b) for(i=a;i>=b;i--) using namespace std; typedef long long ll; const int maxn=100000+10,mo1=1000000007,mo2=998244353; struct dong{ int x,y; friend bool operator <(dong a,dong b){ return a.x<b.x||a.x==b.x&&a.y<b.y; } } zlt,b[maxn]; int h[maxn],go[maxn],next[maxn],a[maxn],dep[maxn],mx[maxn],two[maxn*2][2]; int h2[maxn*2],g2[maxn],n2[maxn],dl[maxn]; int dfn[maxn],low[maxn],tree[maxn*8][2],size[maxn*8]; int i,j,k,l,r,mid,t,x,y,n,m,top,tot,head,tail,now; int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } void add(int x,int y){ go[++tot]=y; next[tot]=h[x]; h[x]=tot; } void dfs(int x){ mx[x]=x; dfn[x]=++top; int t=h[x]; while (t){ dep[go[t]]=dep[x]+1; dfs(go[t]); if (dep[mx[go[t]]]>dep[mx[x]]) mx[x]=mx[go[t]]; t=next[t]; } low[x]=++top; } void add2(int x,int y){ g2[++tot]=y; n2[tot]=h2[x]; h2[x]=tot; } void bfs(){ head=0;tail=1; dl[1]=1; while (head<tail){ now=dl[++head]; t=h[now]; while (t){ dl[++tail]=go[t]; t=next[t]; } } fo(i,1,n) add2(dep[i],i); } void clear(int p,int l,int r){ tree[p][0]=tree[p][1]=size[p]=0; if (l==r) return; int mid=(l+r)/2; clear(p*2,l,mid);clear(p*2+1,mid+1,r); } void change(int p,int l,int r,int a,int b,int f){ if (l==r){ if (f==1){ size[p]=1; tree[p][0]=tree[p][1]=b; } else size[p]=tree[p][0]=tree[p][1]=0; return; } int mid=(l+r)/2; if (a<=mid) change(p*2,l,mid,a,b,f);else change(p*2+1,mid+1,r,a,b,f); tree[p][0]=((ll)tree[p*2][0]*two[size[p*2+1]][0]%mo1+tree[p*2+1][0])%mo1; tree[p][1]=((ll)tree[p*2][1]*two[size[p*2+1]][1]%mo2+tree[p*2+1][1])%mo2; size[p]=size[p*2]+size[p*2+1]; } void query(int p,int l,int r,int a,int b){ if (l==a&&r==b){ zlt.x=((ll)zlt.x*two[size[p]][0]%mo1+tree[p][0])%mo1; zlt.y=((ll)zlt.y*two[size[p]][1]%mo2+tree[p][1])%mo2; return; } int mid=(l+r)/2; if (b<=mid) query(p*2,l,mid,a,b); else if (a>mid) query(p*2+1,mid+1,r,a,b); else{ query(p*2,l,mid,a,mid); query(p*2+1,mid+1,r,mid+1,b); } } bool check(int ans){ clear(1,1,2*n); top=0; fd(i,n,1){ if (i!=n&&dep[dl[i]]!=dep[dl[i+1]]){ t=h2[dep[dl[i]]+ans+1]; while (t){ y=g2[t]; change(1,1,n*2,dfn[y],0,-1); change(1,1,n*2,low[y],0,-1); t=n2[t]; } } x=dl[i]; change(1,1,n*2,dfn[x],1,1); change(1,1,n*2,low[x],0,1); if (dep[x]+ans>dep[mx[x]]) continue; zlt.x=zlt.y=0; query(1,1,n*2,dfn[x],low[x]); b[++top]=zlt; } sort(b+1,b+top+1); fo(i,1,top-1) if (b[i].x==b[i+1].x&&b[i].y==b[i+1].y) return 1; return 0; } int main(){ freopen("ernd.in","r",stdin);freopen("ernd.out","w",stdout); n=read(); two[0][0]=two[0][1]=1; fo(i,1,2*n){ two[i][0]=two[i-1][0]*2%mo1; two[i][1]=two[i-1][1]*2%mo2; } fo(i,1,n){ k=read(); fo(j,1,k) a[j]=read(); fd(j,k,1) add(i,a[j]); } dep[1]=1; dfs(1); tot=0; bfs(); l=0;r=dep[mx[1]]-1; while (l<r){ mid=(l+r+1)/2; if (check(mid)) l=mid;else r=mid-1; } printf("%d\n",l); }
相关文章推荐
- [树的同构][二分][可并堆维护哈希] LOJ#6066 || BZOJ4928 && 2017 山东一轮集训 Day3. 第二题
- [bzoj4930][SDOI省队集训2017]棋盘
- [bzoj4931][SDOI省队集训2017]塔
- [bzoj4927][SDOI省队集训2017]diyiti
- [JZOJ5137][SDOI省队集训2017][bzoj4842]养猫
- [JZOJ5131][SDOI省队集训2017]距离
- [JZOJ5132][SDOI省队集训2017]子序列
- [JZOJ5133][SDOI省队集训2017]重建
- [JZOJ5134][SDOI省队集训2017]三元组
- 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)
- [JZOJ5129][SDOI省队集训2017]字符串
- [JZOJ5130][51nod1446][SDOI省队集训2017]苹果树
- [JZOJ5135][SDOI省队集训2017]逆序对
- [计数][容斥] LOJ#6065 || BZOJ4927 && 2017 山东一轮集训 Day3. 第一题
- [JZOJ5136][SDOI省队集训2017]重排
- 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
- [BZOJ5010][FJOI2017]矩阵填数(状压DP)
- bzoj5010: [Fjoi2017]矩阵填数
- bzoj P4825 [Hnoi2017]单旋——solution
- [最短路 杂题] LOJ#6075. 「2017 山东一轮集训 Day6」重建