传送门:HDU5044
【分析】题目很简单,最容易很出来是树链剖分的模版题,但是时间真的卡的超级紧,优化了一天才过开始使用树链剖分+线段树,常数太大T了,而且HDU编译器升级后连出题人kuangbin的其中一个标程都T了,然后各种尝试,最后把线段树改成树状数组才过,还必须加快速读入才过的,复杂度O(n*logn*logn)。还有种O(n+m)的做法用什么LCA,表示看不懂。
【AC CODE】4321ms
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 100000+10;
char buf[MAXN], *ps = buf, *pe = buf+1;//ps当前读取到的开头指针,pe当前缓存结尾指针
inline void rnext(){
if(++ps == pe)
pe = (ps = buf)+fread(buf,1,sizeof(buf),stdin);
}
inline int in()
{
if(ps == pe) return -INF;
int f = 1;
do{
rnext();
if('-' == *ps) f = -1;
}while(!isdigit(*ps) && ps != pe);
if(ps == pe) return -INF;
int ans = 0;
do
{
ans = (ans<<1)+(ans<<3)+*ps-48;
rnext();
}while(isdigit(*ps) && ps != pe);
return ans*f;
}
inline void in_str(char *ans)
{
int cnt = 0;
do{
rnext();
}while(!('A' == *ps || 'D' == *ps || '1' == *ps || '2' == *ps) && ps != pe);
do{
ans[cnt++] = *ps;
rnext();
}while(('A' == *ps || 'D' == *ps || '1' == *ps || '2' == *ps) && ps != pe);
ans[cnt] = 0;
}
inline void out(LL x) {
if(x>9) out(x/10);
putchar(x%10+'0');
}
int head[MAXN],nxt[MAXN<<1],to[MAXN<<1],tol;
void add_edge(int u, int v)
{
nxt[tol] = head[u],to[tol] = v;
head[u] = tol++;
}
struct TCP1{
private:
int fa[MAXN];//fa[u]表示u的父亲
int dep[MAXN];//dep[u]表示u的深度
int sz[MAXN];//sz[u]表示以u为根的子树有几个结点
int son[MAXN];//son[u]表示u的所有儿子结点中sz[]最大的那个点(重儿子)
int top[MAXN];//top[u]表示u所在的链的顶端节点
int tree[MAXN];//tree[u]表示节点u在线段树中的编号
int pre[MAXN];//pre[x]表示线段树中编号为x的点在树中的点(和tree相反)
int cnt;//线段树中点的数量(其实就是n)
void dfs1(int u)//预处理fa[],dep[],sz[],son[]
{
int num = 0;
sz[u] = 1;
for(int i = head[u]; ~i; i = nxt[i])
{
int v = to[i];
if(v == fa[u]) continue;
fa[v] = u;dep[v] = dep[u]+1;
dfs1(v);
if(sz[v] > num) num = sz[v], son[u] = v;
sz[u] += sz[v];
}
}
void dfs2(int u, int num)//预处理top[],tree[],pre[]
{
tree[u] = ++cnt, pre[cnt] = u,top[u] = num;
if(-1 == son[u]) return;
dfs2(son[u],num);//必须点递归重儿子,否则重链在线段树的编号会不连续
for(int i = head[u]; ~i; i = nxt[i])
{
int v = to[i];
if(v != fa[u] && son[u] != v) dfs2(v,v);
}
}
LL add[MAXN];
inline int lowbit(int x){return x&-x;}
LL sum(int x)
{
LL ans = 0;
while(x > 0)
{
ans += add[x];
x -= lowbit(x);
}
return ans;
}
void update(int x, int num)
{
while(x <= cnt)
{
add[x] += num;
x += lowbit(x);
}
}
public:
void init(int rt)
{
clc(son,-1);
dep[rt] = 0, fa[rt] = -1;
dfs1(rt);
cnt = 0;
dfs2(rt,rt);
clc(add,0);
}
void tcp_update(int x, int y, int v)//树上点x~y之间的权值都加v
{
int f1 = top[x], f2 = top[y];
while(f1 != f2)
{
if(dep[f1] < dep[f2]) swap(f1,f2), swap(x,y);
update(tree[f1],v);update(tree[x]+1,-v);
x = fa[f1], f1 = top[x];
}
x = tree[x], y = tree[y];
if(x > y) swap(x,y);
update(x,v);update(y+1,-v);
}
LL tcp_query(int x)//查询点x的权值
{
return sum(tree[x]);
}
}tcp1;
int e[MAXN][2];
struct TCP2{
private:
int fa[MAXN];//fa[u]表示u的父亲
int dep[MAXN];//dep[u]表示u的深度
int sz[MAXN];//sz[u]表示以u为根的子树有几个结点
int son[MAXN];//son[u]表示u的所有儿子结点中sz[]最大的那个点(重儿子)
int top[MAXN];//top[u]表示u所在的链的顶端节点
int tree[MAXN];//tree[u]表示节点u和他父亲的连边在线段树中的编号
int pre[MAXN];//pre[x]和tree相反
int bnum[MAXN];//每个边转化为深度较深的点之后对应的边号
int cnt;//线段树中点的数量(其实就是n)
void dfs1(int u)//预处理fa[],dep[],sz[],son[]
{
int num = 0;
sz[u] = 1;
for(int i = head[u]; ~i; i = nxt[i])
{
int v = to[i];
if(v == fa[u]) continue;
fa[v] = u;dep[v] = dep[u]+1;
dfs1(v);
if(sz[v] > num) num = sz[v], son[u] = v;
sz[u] += sz[v];
}
}
void dfs2(int u, int num)//预处理top[],tree[],pre[]
{
tree[u] = ++cnt, pre[cnt] = u,top[u] = num;
if(-1 == son[u]) return;
dfs2(son[u],num);//必须点递归重儿子,否则重链在线段树的编号会不连续
for(int i = head[u]; ~i; i = nxt[i])
{
int v = to[i];
if(v != fa[u] && son[u] != v) dfs2(v,v);
}
}
LL add[MAXN];
inline int lowbit(int x){return x&-x;}
LL sum(int x)
{
LL ans = 0;
while(x > 0)
{
ans += add[x];
x -= lowbit(x);
}
return ans;
}
void update(int x, int num)
{
while(x <= cnt)
{
add[x] += num;
x += lowbit(x);
}
}
public:
void init(int rt)
{
clc(son,-1);
dep[rt] = 0, fa[rt] = -1;
dfs1(rt);
cnt = 0;
dfs2(rt,rt);
clc(add,0);
rep(i,1,cnt)//所有边
{
if(dep[e[i][0]] > dep[e[i][1]]) swap(e[i][0],e[i][1]);//每条边在树中下面的点作为标识(边转点)
bnum[e[i][1]] = i;
}
}
void tcp_update(int x, int y, int v)
{
int f1 = top[x], f2 = top[y];
while(f1 != f2)
{
if(dep[f1] < dep[f2]) swap(f1,f2), swap(x,y);
update(tree[f1],v);update(tree[x]+1,-v);
x = fa[f1], f1 = top[x];
}
if(x == y) return;
if(dep[x] > dep[y]) swap(x,y);
update(tree[son[x]],v);update(tree[y]+1,-v);
}
LL tcp_query(int x)
{
return sum(tree[e[x][1]]);//直接用e[c][1](每条边深度较深的点)作为该边的标识
}
}tcp2;
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
int t = in(),count = 0;
while(t--)
{
int n = in(),q = in();
clc(head,-1);
tol = 0;
rep(i,1,n)
{
int u = in(),v = in();
e[i][0] = u, e[i][1] = v;
add_edge(u,v);
add_edge(v,u);
}
tcp1.init(1);
tcp2.init(1);
char op[10];
while(q--)
{
in_str(op);
int x = in(),y = in(),v = in();
if('1' == op[3]) tcp1.tcp_update(x,y,v);
else tcp2.tcp_update(x,y,v);
}
printf("Case #%d:\n",++count);
repe(i,1,n)
{
out(tcp1.tcp_query(i));
putchar(i==n?'\n':' ');
}
rep(i,1,n)
{
out(tcp2.tcp_query(i));
if(i != n-1) putchar(' ');
}
putchar('\n');
}
return 0;
}