题目链接 https://ac.nowcoder.com/acm/contest/6290#question
这题被自己挖了好多坑 一共wa了十一法
好在最后六分钟a掉了
一开始有输出 但是一直wa我还以为是取模的问题
结果wa了六七发 觉得不太对劲
自己造了个数据 发现我建树的时候数据赋值有问题
改了快一个小时 才改过来
后来取模还是过不了 才发现我的mod一直开的是2333 但题目要求的是23333
被自己坑哭
题目思路
题目给出一个树 要对数进行两个操作
1 对结点x的子树所有节点加y(包括x点)
2 查询结点x的子树值的平方和(包括x点)
这题看要求就知道是用线段树维护区间和和区间平方和
区间和很简单 线段树入门操作
区间平方和 也很简单
令区间和为sum 要加的值为y 区间长度为len
他的改变值 其实就是(2*sum * y+len * y * y)(类比完全平方公式)
然后操作二也是线段树基本操作
还要注意的就是题目给的是一棵树上的值
所以要先求出dfs序
并且在建树时赋值要注意 具体事项代码中再写
ac代码
(不是很知道咋去模 所以不要吐槽我沙雕的取模操作 球球了)
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x1f1f1f1f;
const ll llinf =1e17+10;
const int mod = 2333;
ll a[maxn];
struct node
{
int to,next;
}e[maxn*2];
int first[maxn],len,tim,in[maxn],out[maxn],pos[maxn];
ll t[maxn<<2],ans[maxn<<2],lazy[maxn<<2],lens[maxn<<2];
void add(int u,int v)
{
e[len].to=v;
e[len].next=first[u];
first[u]=len++;
}
void dfs(int x,int fa)
{
in[x]=++tim;
pos[in[x]]=x;//这里要标记一下每个位置对应的a数组的位置 方便建树时赋值
for(int i=first[x];i!=-1;i=e[i].next)
{
int to=e[i].to;
if(to==fa)continue;
dfs(to,x);
}
out[x]=tim;
}
void pushup(int rt)
{
t[rt]=t[lson]+t[rson];
ans[rt]=ans[lson]+ans[rson];
}
void pushdown(int rt)
{
if(lazy[rt])
{
lazy[lson]+=lazy[rt];
lazy[rson]+=lazy[rt];
ans[lson]=(ans[lson]%mod+2*lazy[rt]%mod*t[lson]%mod+lens[lson]%mod*lazy[rt]%mod*lazy[rt]%mod)%mod;
ans[rson]=(ans[rson]%mod+2*lazy[rt]%mod*t[rson]%mod+lens[rson]%mod*lazy[rt]%mod*lazy[rt]%mod)%mod;
t[lson]+=lazy[rt]*lens[lson];
t[rson]+=lazy[rt]*lens[rson];
lazy[rt]=0;
}
}
void build(int l,int r,int rt)
{
lens[rt]=r-l+1;
if(l==r)
{
t[rt]=a[pos[l]];//这里之前写a[l] wa了我七发 后来找bug也找到想死 真的要注意这些细节
ans[rt]=a[pos[l]]*a[pos[l]]%mod;
//printf("rt %d %lld\n",pos[l],a[pos[l]]);
return;
}
int mid=(l+r)>>1;
build(l,mid,lson);
build(mid+1,r,rson);
pushup(rt);
}
void change(int l,int r,int L,int R,int rt,ll v)
{
if(L<=l&&r<=R)
{
ans[rt]+=2*v*t[rt]+v*v*lens[rt];
t[rt]+=v*lens[rt];
lazy[rt]+=v;
return;
}
int mid=(l+r)>>1;
pushdown(rt);
if(L<=mid)
change(l,mid,L,R,lson,v);
if(R>mid)
change(mid+1,r,L,R,rson,v);
pushup(rt);
}
ll query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R)
{
return ans[rt];
}
int mid=(l+r)>>1;
pushdown(rt);
ll res=0;
if(L<=mid)
res+=query(l,mid,L,R,lson)%mod;
if(R>mid)
res+=query(mid+1,r,L,R,rson)%mod;
return res;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
int n,q;
ms(first,-1);
ll sum=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,-1);
build(1,n,1);
while(q--)
{
int type,x;
ll y;
scanf("%d",&type);
if(type==1)
{
scanf("%d%lld",&x,&y);
int l=in[x];
int r=out[x];
change(1,n,l,r,1,y);
}
if(type==2)
{
scanf("%d",&x);
int l=in[x];
int r=out[x];
ll sum=query(1,n,l,r,1)%mod;
printf("%lld\n",sum);
}
}
}
写了这么多天线段树 终于在比赛里面自己写出一道线段树的题了
虽然过程非常艰难(主要还是自己演) 但是结果还是好的
证明我这些天啃专题还是有用的 继续加油吧