【BZOJ-4127】Abs 树链剖分 + 线段树 (有趣的姿势)
4127: Abs
Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 381 Solved: 132
[Submit][Status][Discuss]
Description
给定一棵树,设计数据结构支持以下操作
1 u v d 表示将路径 (u,v) 加d
2 u v 表示询问路径 (u,v) 上点权绝对值的和
Input
第一行两个整数n和m,表示结点个数和操作数
接下来一行n个整数a_i,表示点i的权值
接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边
接下来m行,每行一个操作,输入格式见题目描述
Output
对于每个询问输出答案
Sample Input
-4 1 5 -2
1 2
2 3
3 4
2 1 3
1 1 4 3
2 1 3
2 3 4
Sample Output
13
9
HINT
对于100%的数据,n,m <= 10^5 且 0<= d,|a_i|<= 10^8
Source
Solution
树链剖分显然,把树上路径问题转化为序列问题
然后线段树维护区间权值绝对值和,支持区间加
注意Delta>=0这个条件,即1操作保证加数不为负,即实际值不发生减小
于是线段树维护一些东西:
l,r左右端点;maxf区间最大负数;num区间正数个数-负数个数;tag区间加的标记;sum区间绝对值和
maxf的意义在于,对于区间加Delta,那么如果maxf+Delta<0很显然1操作后会出现变号的情况,对于维护绝对值和必然会做出影响,所以用来进行判断
num的意义在于计算sum的变化,这里同样可以考虑维护正数个数和负数个数,但Code起来比较不方便
tag的意义在于,如果区间+Delta不发生变号情况(即maxf+Delta<0||maxf>=0)的时候,可以直接打上标记,否则则需要把标记下放至叶节点,在向上更新答案
Code
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; 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; } #define maxn 110000 int n,m,a[maxn]; struct Edgenode{int to,next;}edge[maxn<<1]; int head[maxn],cnt=1; void add(int u,int v){cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;} void insert(int u,int v){add(u,v); add(v,u);} //---------------------------------------------------------------------------------- int size[maxn],fa[maxn],deep[maxn],son[maxn],pl[maxn],sz,pre[maxn],top[maxn],pr[maxn]; void dfs_1(int now) { size[now]=1; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=fa[now]) { fa[edge[i].to]=now; deep[edge[i].to]=deep[now]+1; dfs_1(edge[i].to); if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to; size[now]+=size[edge[i].to]; } } void dfs_2(int now,int chain) { pl[now]=++sz; pre[sz]=a[now]; top[now]=chain; if (son[now]) dfs_2(son[now],chain); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=son[now] && edge[i].to!=fa[now]) dfs_2(edge[i].to,edge[i].to); pr[now]=sz; } //---------------------------------------------------------------------------------- struct TreeNode { int l,r;long long maxf,tag,sum,num; void Add(int k) { if (k<0) maxf=k,sum=-k,num=-1; else maxf=0,sum=k,num=1; tag=0; } }tree[maxn<<2]; long long Maxf(long long x,long long y) { if (x>=0 && y>=0) return 0; if (x>=0 || y>=0) return min(x,y); return max(x,y); } void Update(int now) { tree[now].maxf=Maxf(tree[now<<1].maxf,tree[now<<1|1].maxf); tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum; tree[now].num=tree[now<<1].num+tree[now<<1|1].num; } void BuildTree(int now,int l,int r) { tree[now].l=l,tree[now].r=r; if (l==r) {tree[now].Add(pre[l]); return;} int mid=(l+r)>>1; BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r); Update(now); } void Pushdown(int now) { if (!tree[now].tag || tree[now].l==tree[now].r) return; int tag=tree[now].tag; tree[now].tag=0; tree[now<<1].maxf+=tag; tree[now<<1].sum+=tree[now<<1].num*tag; tree[now<<1].tag+=tag; tree[now<<1|1].maxf+=tag; tree[now<<1|1].sum+=tree[now<<1|1].num*tag; tree[now<<1|1].tag+=tag; } void Change(int now,int L,int R,int D) { int l=tree[now].l,r=tree[now].r; if (L<=l && R>=r && (tree[now].maxf>=0 || tree[now].maxf+D<0)) {tree[now].maxf+=D; tree[now].sum+=(long long)tree[now].num*D; tree[now].tag+=D; return;} if (l==r) {tree[now].Add(tree[now].maxf+D); return;} Pushdown(now); int mid=(l+r)>>1; if (L<=mid) Change(now<<1,L,R,D); if (R>mid) Change(now<<1|1,L,R,D); Update(now); } long long Query(int now,int L,int R) { Pushdown(now); int l=tree[now].l,r=tree[now].r; if (L<=l && R>=r) return tree[now].sum; int mid=(l+r)>>1; long long re=0; if (L<=mid) re+=Query(now<<1,L,R); if (R>mid) re+=Query(now<<1|1,L,R); return re; } void DeBug(int now) { int l=tree[now].l,r=tree[now].r; if (l==r) {printf("l==r=%d Val=%d maxf=%lld tag=%lld sum=%lld num=%lld\n",l,a[l],tree[now].maxf,tree[now].tag,tree[now].sum,tree[now].num);return;} int mid=(l+r)>>1; DeBug(now<<1); DeBug(now<<1|1); } //---------------------------------------------------------------------------------- void Solve_1(int x,int y,int D) { while (top[x]!=top[y]) { if (deep[top[x]]<deep[top[y]]) swap(x,y); Change(1,pl[top[x]],pl[x],D); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); Change(1,pl[x],pl[y],D); } long long Solve_2(int x,int y) { long long re=0; while (top[x]!=top[y]) { if (deep[top[x]]<deep[top[y]]) swap(x,y); re+=Query(1,pl[top[x]],pl[x]); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); re+=Query(1,pl[x],pl[y]); return re; } //---------------------------------------------------------------------------------- int main() { // freopen("4127.in","r",stdin); // freopen("4127.out","w",stdout); n=read(),m=read(); for (int i=1; i<=n; i++) a[i]=read(); for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v); dfs_1(1); dfs_2(1,1); BuildTree(1,1,n); int opt,u,v,w; while (m--) { opt=read(); // DeBug(1); if (opt==1) u=read(),v=read(),w=read(),Solve_1(u,v,w); else u=read(),v=read(),printf("%lld\n",Solve_2(u,v)); } return 0; }
友情附送数据生成器:(Designed by YveH)
#include<ctime> #include<cstdio> #include<cstdlib> using namespace std; int main() { freopen("4127.in","w",stdout); srand(time(0)); int n=1000,q=10000; printf("%d %d\n",n,q); for (int i=1;i<=n;i++) printf("%d ",rand()%10000-5000); printf("\n"); for (int i=2;i<=n;i++) printf("%d %d\n",i,rand()%(i-1)+1); for (int i=1;i<=q;i++) { int opt=rand()%2+1; printf("%d ",opt); if (opt==1) printf("%d %d %d\n",rand()%n+1,rand()%n+1,rand()%10000); if (opt==2) printf("%d %d\n",rand()%n+1,rand()%n+1); } return 0; }
友情附送对拍:(Designed by YveH)
#include<iostream> #include<cstdio> #include<windows.h> using namespace std; int main() { while (1) { system("4127data.exe"); system("4127.exe"); system("4127STD.exe"); if (system("fc 4127.out 4127std.out")) break; } return 0; }
这道破题,两天前YveH和Etienne写了半天多,DCrusher大爷嘲讽他们,我替他们不服,然后自己果断写了1小时,然后调了3小时....发现自信写不错的链剖少打了一句话MDZZ
(加上省队集训,第三题暴力打到70%就去吃饭了,回来懒得打了,体验了连续滚粗的快感)
发现自己的常数已经接近Etienne了...就慢个100ms不到