Codechef EDGEST 树套树 树状数组 线段树 LCA 卡常
原文链接http://www.cnblogs.com/zhouzhendong/p/9016579.html
题目传送门 - Codechef EDGEST
题意
给定相同点集上的两棵生成树$T_1$和$T_2$,节点编号为$1$∼$N$。对于$T_1$中的每条边$e_1$,你需要求在$T_2$中有多少条边$e_2$满足:
• $T_1 − e_1 + e_2$(从$T_1$中删去$e_1$再加上$e_2$构成的图)是一棵生成树;
• $T_2 − e_2 + e_1$ 也是一棵生成树。
$1 ≤ T ≤ 10 , 2 ≤ N ≤ 2 \times 10^5 , ∑N ≤ 2 \times 10^5$
题解
首先,给这两棵无根树定根为$1$。
给$T_1$进行$dfs$,设“时间”表示当前已经访问的节点数,记节点$i$第一次访问的时间为$in_i$(即其$dfs$序)(要计入节点$i$),退出节点$i$的时间为$out_i$。
考虑删除$T_1$的一条边$y_1\rightarrow x_1$,其中$y_1$为$x_1$的父亲。
(1)则需要添加的边必定是$x_2$这棵子树中的节点对这棵子树外的节点的连边。即$\forall e\in T_2 , e=(x_2,y_2)$且($(in_{x_2}\in [in_{x_1},out_{x_1}]且in_{y_2}\in [1,in_{x_1})\cup(out_{x_1},n])$或$(in_{y_2}\in [in_{x_1},out_{x_1}]且in_{x_2}\in [1,in_{x_1})\cup(out_{x_1},n])$)。
考虑在$T_2$中,连接了$x_2$和$y_2$,则要删除的必定是$x_1$到$y_1$路径上的边。
其中,$x_1$到$y_1$的路径可以拆分成$x_1$到$T_2$根路径、$y_1$到根路径,以及两条$LCA_{T_2}(x_1,y_1)$到根的负路径。
在$T_1$中,我们要计算$x_1$这棵子树中的节点对这棵子树外的节点的连边,并且这个连边在$T_2$中存在。
于是梳理完上面的这些东西之后,我们可以开始说做法了。
我们用树套树维护(1)要弄的东西。
首先预处理在$T_1$中的每一对$(x_1,y_1)$需要查询的询问区间(即(1)中所指)、询问($T_2$中)哪一个节点$(x_1,y_1和LCA_{T_2}(x_1,y_1)$到根路径的贡献,把这些询问分别放到对应节点所准备的$vector$里面。
然后对$T2$进行$dfs$,对于$T_2$的每一个节点,每一个询问分别用当前树套树状态来查询,并在$dfs$的过程中修改树套树。
时间复杂度$O(n\log^2 n)$。但是常数较大,要$TLE$。
于是我们把树套树的第一维写成树状数组,再卡一波常数就可以通过了。
代码长到吐。
代码
#include <bits/stdc++.h>
#pragma GCC optimize ("Ofast")
#define time __time
using namespace std;
const int N=200005;
int lowbit(int x){
return x&-x;
}
__inline char gc() {
static char buf[300000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 300000, stdin), p1 == p2) ? EOF : *p1++;
}
//#define gc getchar
__inline int read(){
int x=0;
char ch=gc();
while (!isdigit(ch))
ch=gc();
while (isdigit(ch))
x=(x<<1)+(x<<3)+ch-48,ch=gc();
return x;
}
int T,n,root[N],fa[N],in[N],out[N],time,ans[3][N];
int anst[N][20],depth[N];
struct pr{
int first,second;
pr(){}
pr(int a,int b){
first=a,second=b;
}
};
vector <pr> q[N];
struct Gragh{
int cnt,x[N*2],y[N*2],nxt[N*2],fst[N];
void clear(){
cnt=0;
memset(fst,0,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,x[cnt]=a,nxt[cnt]=fst[a],fst[a]=cnt;
}
}a,b;
struct Node{
int v,lc,rc;
}t[N*800];
int STsize;
__inline void update(int &rt,int L,int R,int x,int d){
if (!rt){
rt=++STsize;
t[rt].v=t[rt].lc=t[rt].rc=0;
}
t[rt].v+=d;
if (L==R)
return;
int mid=(L+R)>>1;
if (x<=mid)
update(t[rt].lc,L,mid,x,d);
else
update(t[rt].rc,mid+1,R,x,d);
}
__inline int query(int &rt,int L,int R,int xL,int xR){
if (!rt||L>xR||R<xL)
return 0;
if (xL<=L&&R<=xR)
return t[rt].v;
int mid=(L+R)>>1;
if (xR<=mid)
return query(t[rt].lc,L,mid,xL,xR);
if (xL>mid)
return query(t[rt].rc,mid+1,R,xL,xR);
return query(t[rt].lc,L,mid,xL,mid)+query(t[rt].rc,mid+1,R,mid+1,xR);
}
__inline void update(int rt,int L,int R,int x,int y,int d){
for (int i=x;i<=n;i+=i&-i)
update(root[i],1,n,y,d);
}
__inline int query(int rt,int L,int R,int xL,int xR,int yL,int yR){
int ans=0;
for (int i=xR;i;i-=i&-i)
ans+=query(root[i],1,n,yL,yR);
for (int i=xL-1;i;i-=i&-i)
ans-=query(root[i],1,n,yL,yR);
return ans;
}
__inline void change(int x,int y,int d){
x=in[x],y=in[y];
if (x>y)
swap(x,y);
update(1,1,n,x,y,d);
}
__inline int ask(int x,int y){
int ans=0;
if (1<=x-1)
ans+=query(1,1,n,1,x-1,x,y);
if (y+1<=n)
ans+=query(1,1,n,x,y,y+1,n);
return ans;
}
__inline void LCA_prepare(int x,int pre){
depth[x]=depth[pre]+1;
anst[x][0]=pre;
for (int i=1;i<20;i++)
anst[x][i]=anst[anst[x][i-1]][i-1];
for (int i=b.fst[x];i;i=b.nxt[i])
if (b.y[i]!=pre)
LCA_prepare(b.y[i],x);
}
__inline int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
for (int i=19;i>=0;i--)
if (depth[anst[x][i]]>=depth[y])
x=anst[x][i];
if (x==y)
return x;
for (int i=19;i>=0;i--)
if (anst[x][i]!=anst[y][i])
x=anst[x][i],y=anst[y][i];
return anst[x][0];
}
__inline void dfsa(int x,int pre){
fa[x]=pre;
in[x]=++time;
if (pre){
q[x].push_back(pr(x,0));
q[pre].push_back(pr(x,1));
q[LCA(x,pre)].push_back(pr(x,2));
}
for (int i=a.fst[x];i;i=a.nxt[i])
if (a.y[i]!=pre)
dfsa(a.y[i],x);
out[x]=time;
}
__inline void dfsb(int x,int pre){
if (pre)
change(x,pre,1);
/* for (int i=0;i<q[x].size();i++){
int y=q[x][i].first,t=q[x][i].second;
ans[t][y]=ask(in[y],out[y]);
}*/
while (!q[x].empty()){
int y=q[x].back().first,t=q[x].back().second;
ans[t][y]=ask(in[y],out[y]);
q[x].pop_back();
}
for (int i=b.fst[x];i;i=b.nxt[i])
if (b.y[i]!=pre)
dfsb(b.y[i],x);
if (pre)
change(x,pre,-1);
}
int main(){
T=read();
while (T--){
n=read();
STsize=0;
memset(root,0,sizeof root);
a.clear(),b.clear();
for (int i=1,x,y;i<n;i++){
x=read(),y=read();
a.add(x,y),a.add(y,x);
}
for (int i=1,x,y;i<n;i++){
x=read(),y=read();
b.add(x,y),b.add(y,x);
}
for (int i=0;i<=n;i++)
for (int j=0;j<20;j++)
anst[i][j]=0;
LCA_prepare(1,0);
for (int i=1;i<=n;i++)
q[i].clear();
time=0;
dfsa(1,0);
dfsb(1,0);
for (int i=1;i<=(n-1)*2;i+=2){
int x=a.x[i],y=a.y[i];
if (fa[y]==x)
swap(x,y);
printf("%d ",ans[0][x]+ans[1][x]-2*ans[2][x]);
}
puts("");
}
return 0;
}
Codechef EDGEST 树套树 树状数组 线段树 LCA 卡常的更多相关文章
- 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树
正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...
- 树状数组 && 线段树应用 -- 求逆序数
参考:算法学习(二)——树状数组求逆序数 .线段树或树状数组求逆序数(附例题) 应用树状数组 || 线段树求逆序数是一种很巧妙的技巧,这个技巧的关键在于如何把原来单纯的求区间和操作转换为 求小于等于a ...
- hdu1394(枚举/树状数组/线段树单点更新&区间求和)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 ...
- hdu 5147 Sequence II【树状数组/线段树】
Sequence IITime Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem ...
- hdu 1166:敌兵布阵(树状数组 / 线段树,入门练习题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- 【bzoj4785】[Zjoi2017]树状数组 线段树套线段树
题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作 ...
- BZOJ_1901_&_ZJU_2112_Dynamic_Rankings_(主席树+树状数组/线段树+(Treap/Splay))
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1901 给出一个长度为n的数列A,有m次询问,询问分两种:1.修改某一位置的值;2.求区间[l, ...
- 数据结构--树状数组&&线段树--基本操作
随笔目的:方便以后对树状数组(BIT)以及基本线段树的回顾 例题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 例题:hdu 1166 敌兵布阵 T ...
- BZOJ 3333 排队计划 树状数组+线段树
题目大意:给定一个序列.每次选择一个位置,把这个位置之后全部小于等于这个数的数抽出来,排序,再插回去,求每次操作后的逆序对数 首先我们每一次操作 对于这个位置前面的数 因为排序的数与前面的数位置关系不 ...
随机推荐
- springcloud-1: 用官方的pom.xml配置添加依赖失败
在eclipse中用STS生成了一个springcloud应用,pom.xml的核心配置如下: <parent> <groupId>org.springframework.bo ...
- python-函数入门(二)
一.函数对象 什么是函数? 函数是第一类对象,指的是函数名指向的值(函数)可以被当做数据去使用 1.函数的特性 1.函数可以被引用,即函数可以把值赋值给一个变量 def foo(): print('f ...
- destoon使用
使用小计 1.判断是否是手机端 {$DT_TOUCH}模板中使用 2.判断句 {if} {/if} 3.表单管理 扩展功能-----表单管理:添加表单---->管理表单选项------> ...
- 安装python的pip模块
安装python的pip模块 网址https://pypi.python.org/pypi/pip 选择,点击下载 将文件解压到C:\Users\Administrator\AppData\Local ...
- Js:消息弹出框、获取时间区间、时间格式、easyui datebox 自定义校验、表单数据转化json、控制两个日期不能只填一个
(function ($) { $.messageBox = function (message) { $.messager.show({ title:'消息框提示', msg:message, sh ...
- flask 中orm关系映射 sqlalchemy的查询
flask的orm框架(SQLAlchemy)-一对多查询以及多对多查询 一对多,多对多是什么? 一对多.例如,班级与学生,一个班级对应多个学生,或者多个学生对应一个班级. 多对多.例如,学生与课 ...
- LeetCode(119):杨辉三角 II
Easy! 题目描述: 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行. 在杨辉三角中,每个数是它左上方和右上方的数的和. 示例: 输入: 3 输出: [1,3,3,1] 进阶: ...
- 课外知识----ini
ini 初始化英文单词的缩写,用来初始化参数 ini文件配置 [小节] 键=值 [小节] 键=值
- 性能测试四十二:sql案例之联合索引最左前缀
联合索引:一个索引同时作用于多个字段 联合索引的最左前缀: A.B.C3个字段--联合索引 这个时候,可以使用的查询条件有:A.A+B.A+C.A+B+C,唯独不能使用B+C,即最左侧那个字段必须匹配 ...
- HTMLTestRunner 美化版本
前言 最近小伙伴们在学玩python,,看着那HTMLTestRunner生成的测试报告,左右看不顺眼,终觉得太丑.搜索了一圈没有找到合适的美化报告,于是忍不住自已动手进行了修改,因习惯python ...