【做题记录】2025刷题计划--线段树
A. 「SDOI2014」旅行
给每个宗教开一棵线段树,树剖 \(+\) 线段树单点修改区间查询即可。需要动态开点。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define int ll
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,maxm=5e6+5;
int n,m,a[maxn],b[maxn],cnt;
int top[maxn],fa[maxn],dfn[maxn];
int sz[maxn],hes[maxn],dep[maxn];
vector<int> e[maxn];
il void dfs1(int u){
dep[u]=dep[fa[u]]+1;
sz[u]=1;
int mxs=0;
for(int v:e[u]){
if(v==fa[u]){
continue;
}
fa[v]=u;
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v];
hes[u]=v;
}
}
}
il void dfs2(int u){
dfn[u]=++cnt;
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(int v:e[u]){
if(v==fa[u]||v==hes[u]){
continue;
}
dfs2(v);
}
}
int rt[maxn],ls[maxm],rs[maxm];
int tot,sum[maxm],mx[maxm];
il void pushup(int id){
sum[id]=sum[ls[id]]+sum[rs[id]];
mx[id]=max(mx[ls[id]],mx[rs[id]]);
}
il void upd(int &id,int l,int r,int pos,int val){
if(!id){
id=++tot;
}
if(l==r){
sum[id]=mx[id]=val;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid){
upd(ls[id],l,mid,pos,val);
}
else{
upd(rs[id],mid+1,r,pos,val);
}
pushup(id);
}
il int qsum(int id,int L,int R,int l,int r){
if(!id){
return 0;
}
if(L>=l&&R<=r){
return sum[id];
}
int mid=(L+R)>>1,res=0;
if(l<=mid){
res+=qsum(ls[id],L,mid,l,r);
}
if(r>mid){
res+=qsum(rs[id],mid+1,R,l,r);
}
return res;
}
il int qmx(int id,int L,int R,int l,int r){
if(!id){
return 0;
}
if(L>=l&&R<=r){
return mx[id];
}
int mid=(L+R)>>1,res=0;
if(l<=mid){
res=max(res,qmx(ls[id],L,mid,l,r));
}
if(r>mid){
res=max(res,qmx(rs[id],mid+1,R,l,r));
}
return res;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/10485796.0;}
}
signed main(){
read(n)read(m);
for(int i=1;i<=n;i++){
read(a[i])read(b[i]);
}
for(int i=1,u,v;i<n;i++){
read(u)read(v);
e[u].pb(v),e[v].pb(u);
}
dfs1(1),dfs2(1);
for(int i=1;i<=n;i++){
upd(rt[b[i]],1,n,dfn[i],a[i]);
}
while(m--){
char opt[5];
int u,v;
scanf(" %s",opt+1);
read(u)read(v);
switch(opt[2]){
case 'C':{
upd(rt[b[u]],1,n,dfn[u],0);
b[u]=v;
upd(rt[b[u]],1,n,dfn[u],a[u]);
break;
}
case 'W':{
a[u]=v;
upd(rt[b[u]],1,n,dfn[u],a[u]);
break;
}
case 'S':{
int res=0,x=b[u];
while(top[u]!=top[v]){
// puts("666");
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
res+=qsum(rt[x],1,n,dfn[top[u]],dfn[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
res+=qsum(rt[x],1,n,dfn[u],dfn[v]);
printf("%lld\n",res);
break;
}
default:{
int res=0,x=b[u];
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
res=max(res,qmx(rt[x],1,n,dfn[top[u]],dfn[u]));
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
res=max(res,qmx(rt[x],1,n,dfn[u],dfn[v]));
printf("%lld\n",res);
break;
}
}
}
return 0;
}
}
signed main(){return asbt::main();}
B. 康娜的线段树
对于每个叶子节点,设深度为 \(dep\),则对答案的贡献为 \(a_i\times\sum_{i=0}^{dep}\frac{1}{2^{i}}=a_i\times(2-\frac{1}{2^{dep}})\)。对此做前缀和即可。复杂度线性。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define gcd __gcd
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e6+5;
int n,m,num,a[maxn];
int sum[maxn],pw2[25];
il void build(int id,int l,int r,int cur){
if(l==r){
sum[l]=2*pw2[20]-pw2[20]/pw2[cur];
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid,cur+1);
build(id<<1|1,mid+1,r,cur+1);
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
read(n)read(m)read(num);
for(int i=1;i<=n;i++){
read(a[i]);
}
pw2[0]=1;
for(int i=1;i<=20;i++){
pw2[i]=pw2[i-1]*2;
}
build(1,1,n,0);
int ans=0;
for(int i=1;i<=n;i++){
ans+=a[i]*sum[i];
sum[i]+=sum[i-1];
}
int tmp=gcd(num,pw2[20]);
num/=tmp,pw2[20]/=tmp;
while(m--){
int l,r,val;
read(l)read(r)read(val);
ans+=(sum[r]-sum[l-1])*val;
printf("%lld\n",ans*num/pw2[20]);
}
return 0;
}
}
signed main(){return asbt::main();}
C. [JSOI2009] 等差数列
将原数组差分,用线段树维护选/不选最左边的点、选/不选最右边的点的答案,pushup
转移即可。
注意此处线段树维护的是差分数组,换句话说线段树中长为 \(a\) 的区间在原数组中应为 \(a+1\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5;
int n,m,a[maxn];
struct node{
int lc,rc,num[2][2];
node(){
lc=rc=num[0][0]=num[0][1]=num[1][0]=num[1][1]=0;
}
il int*operator[](const int &x){
return num[x];
}
il node operator+(node x)const{
node res;
res.lc=lc,res.rc=x.rc;
int flag=rc==x.lc;
res[0][0]=min({num[0][1]+x[1][0]-flag,num[0][0]+x[1][0],num[0][1]+x[0][0]});
res[0][1]=min({num[0][1]+x[1][1]-flag,num[0][0]+x[1][1],num[0][1]+x[0][1]});
res[1][0]=min({num[1][1]+x[1][0]-flag,num[1][0]+x[1][0],num[1][1]+x[0][0]});
res[1][1]=min({num[1][1]+x[1][1]-flag,num[1][0]+x[1][1],num[1][1]+x[0][1]});
return res;
}
};
struct stree{
#define lid id<<1
#define rid id<<1|1
#define lc(id) tr[id].lc
#define rc(id) tr[id].rc
node tr[maxn<<2];
int tag[maxn<<2];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int val){
lc(id)+=val,rc(id)+=val;
tag[id]+=val;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void build(int id,int l,int r){
if(l==r){
lc(id)=rc(id)=a[l];
tr[id][0][1]=tr[id][1][0]=tr[id][1][1]=1;
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void upd(int id,int L,int R,int l,int r,int val){
if(L>=l&&R<=r){
pushtag(id,val);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,val);
}
if(r>mid){
upd(rid,mid+1,R,l,r,val);
}
pushup(id);
}
il node query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
pushdown(id);
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
#undef lid
#undef rid
#undef lc
#undef rc
}S;
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int i=1;i<n;i++){
a[i]=a[i+1]-a[i];
}
S.build(1,1,n-1);
read(m);
while(m--){
char opt;
scanf(" %c",&opt);
if(opt=='A'){
int l,r,a,b;
read(l)read(r)read(a)read(b);
if(l>1){
S.upd(1,1,n-1,l-1,l-1,a);
}
if(r>l){
S.upd(1,1,n-1,l,r-1,b);
}
if(r<n){
S.upd(1,1,n-1,r,r,-a-(r-l)*b);
}
}
else{
int l,r;
read(l)read(r);
if(l==r){
puts("1");
continue;
}
printf("%lld\n",S.query(1,1,n-1,l,r-1)[1][1]);
}
}
return 0;
}
}
signed main(){return asbt::main();}
D. [USACO04DEC] Dividing the Path G
设 \(dp_i\) 表示覆盖 \([0,i]\) 需要的最小喷灌器数。方程是好推的。
考虑怎么满足奶牛的限制,发现只要满足对于一个奶牛的区间 \([l,r]\),\([l+1,r-1]\) 都不是某个喷水器覆盖的区间就行了。这可以用差分 \(O(m)\) 完成。
然后暴力 dp 是 \(10^9\) 的,然而可以直接通过。以下是暴力代码。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e3+5,maxm=1e6+5;
const int inf=0x3f3f3f3f;
int n,m,a,b,dp[maxm],flag[maxm];
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m)read(a)read(b);
for(int i=1,l,r;i<=n;i++){
read(l)read(r);
flag[l+1]++,flag[r]--;
}
for(int i=1;i<=m;i++){
flag[i]+=flag[i-1];
}
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(int i=2;i<=m;i+=2){
if(flag[i]){
continue;
}
for(int j=max(i-2*b,0);j<=i-2*a;j++){
dp[i]=min(dp[i],dp[j]+1);
}
}
printf("%d",dp[m]<inf?dp[m]:-1);
return 0;
}
}
int main(){return asbt::main();}
然而虽然这个暴力似乎无法被卡掉这个暴力的复杂度是不对的,考虑优化。啊当然可以线段树优化,但是单调队列岂不是更好写(时间还快)?
以下是优化代码:
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e3+5,maxm=1e6+5;
const int inf=0x3f3f3f3f;
int n,m,a,b,dp[maxm];
int flag[maxm],q[maxm],hd,tl;
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m)read(a)read(b);
for(int i=1,l,r;i<=n;i++){
read(l)read(r);
flag[l+1]++,flag[r]--;
}
for(int i=1;i<=m;i++){
flag[i]+=flag[i-1];
}
memset(dp,0x3f,sizeof dp);
dp[0]=0;
a<<=1,b<<=1;
for(int i=a;i<=m;i+=2){
while(hd<=tl&&i-q[hd]>b){
hd++;
}
while(hd<=tl&&dp[q[tl]]>=dp[i-a]){
tl--;
}
q[++tl]=i-a;
if(!flag[i]){
dp[i]=dp[q[hd]]+1;
}
}
printf("%d",dp[m]<inf?dp[m]:-1);
return 0;
}
}
int main(){return asbt::main();}
E. Chain Queries
一个处理链的十分巧妙的 Trick。
我们发现,对于链中间的点,度数为 \(2\),而两端的点度数为 \(1\)。然而度数是不好维护的,因为一个点改变会导致它的所有儿子都改变。因此我们考虑让每个点都只影响它的父亲。
令 \(u\) 为黑点,我们给 \(u\) 的权值 \(+1\),给 \(fa_u\) 的权值 \(-1\)。于是判断为链的情况只有两种:
- 链从上到下。此时应该只有一个点权值为 \(1\),和一个点权值为 \(-1\)。
- 链从上向两个方向向下。此时权值为 \(1\),\(-1\) 的点都有 \(2\) 个,且那两个为 \(1\) 的中只有一个是黑点,且这两个为 \(1\) 的点还得相邻。
开两个 set
维护即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5;
int T,n,m,a[maxn],b[maxn],fa[maxn];
vector<int> e[maxn];
il void dfs(int u){
for(int v:e[u]){
if(v==fa[u]){
continue;
}
fa[v]=u;
dfs(v);
}
}
set<int> _1,__1;
template<typename T>il void upd(int u,T cal){
if(b[u]==1){
_1.erase(u);
}
else if(b[u]==-1){
__1.erase(u);
}
b[u]=cal(b[u]);
if(b[u]==1){
_1.insert(u);
}
else if(b[u]==-1){
__1.insert(u);
}
}
il int add(int x){
return x+1;
}
il int del(int x){
return x-1;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
// freopen("E.out","w",stdout);
read(T);
while(T--){
read(n)read(m);
int cnt=0;
for(int i=1;i<=n;i++){
read(a[i]);
if(a[i]){
cnt++;
}
}
for(int i=1,u,v;i<n;i++){
read(u)read(v);
e[u].pb(v),e[v].pb(u);
}
dfs(1);
for(int i=1;i<=n;i++){
if(a[i]){
b[i]--,b[fa[i]]++;
}
}
_1.clear(),__1.clear();
for(int i=0;i<=n;i++){
if(b[i]==1){
_1.insert(i);
}
else if(b[i]==-1){
__1.insert(i);
}
}
while(m--){
int u;
read(u);
if(a[u]){
upd(u,add),upd(fa[u],del);
a[u]=0,cnt--;
}
else{
upd(u,del),upd(fa[u],add);
a[u]=1,cnt++;
}
puts(cnt==1||_1.size()==1&&__1.size()==1||_1.size()==2&&__1.size()==2&&((a[*_1.begin()]&&*_1.rbegin()==fa[*_1.begin()])^(a[*_1.rbegin()]&&*_1.begin()==fa[*_1.rbegin()]))?"Yes":"No");
}
for(int i=0;i<=n;i++){
b[i]=0;
e[i].clear();
}
}
return 0;
}
}
int main(){return asbt::main();}
代码中判断的那个 cnt==1
是可以省掉的。
F. Two Subarrays
首先考虑 dp。设:
- \(dp_{i,0}\) 为到第 \(i\) 个位置还没有开始选的答案。
- \(dp_{i,1}\) 为第 \(i\) 个位置是第一段区间的开头或中间的答案。
- \(dp_{i,2}\) 为第 \(i\) 个位置是第一段区间的结尾或在第一、二段区间之间的答案。
- \(dp_{i.3}\) 为第 \(i\) 个位置是第二段区间的开头或中间的答案。
- \(dp_{i,4}\) 为第 \(i\) 个位置是第二段区间的结尾或在第二段区间之后的答案。
于是有转移方程:
于是可以构造出转移矩阵。
用线段树维护即可。矩阵的合并显然不能是矩阵乘法,而应该是相加取 \(\max\)。这也叫广义矩阵乘。
时间复杂度为 \(O(mk^3\log n)\),其中 \(k\) 为矩阵的大小,即为 \(5\)。好吧似乎比较大,甚至还有线段树大常数,但就是过了(不知道不用快读能不能过)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il T read(){
char ch=getchar();
bool fu=0;
while(ch<'0'||ch>'9'){
fu^=ch=='-';
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return fu?-x:x;
}
#undef getchar
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,bool typ=1){
if(x<0){
putchar('-');
x=-x;
}
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
putchar(typ?'\n':' ');
}
#undef putchar
}
using IO::read;
using IO::write;
const int maxn=2e5+5;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n,m;
ll a[maxn],b[maxn];
struct node{
ll mat[5][5];
il void init(){
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
mat[i][j]=-inf;
}
}
}
il ll*operator[](int x){
return mat[x];
}
il node operator+(node x)const{
node res;
res.init();
for(int i=0;i<5;i++){
for(int k=0;k<5;k++){
if(mat[i][k]<=-inf){
continue;
}
for(int j=0;j<5;j++){
if(x[k][j]<=-inf){
continue;
}
res[i][j]=max(res[i][j],mat[i][k]+x[k][j]);
}
}
}
return res;
}
}tr[maxn<<2];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int p){
node &t=tr[id];
t.init();
t[0][0]=0;
t[0][1]=a[p]+b[p];
t[1][1]=a[p];
t[0][2]=a[p]+2*b[p];
t[1][2]=a[p]+b[p];
t[2][2]=0;
t[2][3]=a[p]+b[p];
t[3][3]=a[p];
t[2][4]=a[p]+2*b[p];
t[3][4]=a[p]+b[p];
t[4][4]=0;
}
il void build(int id,int l,int r){
if(l==r){
pushtag(id,l);
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void upd(int id,int l,int r,int pos){
if(l==r){
pushtag(id,l);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid){
upd(lid,l,mid,pos);
}
else{
upd(rid,mid+1,r,pos);
}
pushup(id);
}
il node query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i++){
b[i]=read();
}
build(1,1,n);
m=read();
while(m--){
int opt=read(),l=read(),r=read();
switch(opt){
case 1:{
a[l]=r;
upd(1,1,n,l);
break;
}
case 2:{
b[l]=r;
upd(1,1,n,l);
break;
}
default:{
write(query(1,1,n,l,r)[0][4]);
break;
}
}
}
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
G. [SHOI2012] 魔法树
简单树剖板子题,\(O(m\log^2n)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5;
int n,m,fa[maxn];
int dfn[maxn],cnt;
int sz[maxn],hes[maxn];
int top[maxn],dep[maxn];
vector<int> e[maxn];
il void dfs1(int u){
sz[u]=1;
dep[u]=dep[fa[u]]+1;
int mxs=0;
for(int v:e[u]){
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v];
hes[u]=v;
}
}
}
il void dfs2(int u){
dfn[u]=++cnt;
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(int v:e[u]){
if(v!=hes[u]){
dfs2(v);
}
}
}
ll sum[maxn<<2],tag[maxn<<2];
il void pushup(int id){
sum[id]=sum[lid]+sum[rid];
}
il void pushtag(int id,int l,int r,ll val){
tag[id]+=val;
sum[id]+=val*(r-l+1);
}
il void pushdown(int id,int l,int r){
if(tag[id]){
int mid=(l+r)>>1;
pushtag(lid,l,mid,tag[id]);
pushtag(rid,mid+1,r,tag[id]);
tag[id]=0;
}
}
il void upd(int id,int L,int R,int l,int r,ll val){
if(L>=l&&R<=r){
pushtag(id,L,R,val);
return ;
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,val);
}
if(r>mid){
upd(rid,mid+1,R,l,r,val);
}
pushup(id);
}
il ll query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return sum[id];
}
pushdown(id,L,R);
int mid=(L+R)>>1;
ll res=0;
if(l<=mid){
res+=query(lid,L,mid,l,r);
}
if(r>mid){
res+=query(rid,mid+1,R,l,r);
}
return res;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n);
for(int i=1,u,v;i<n;i++){
read(u)read(v);
fa[v]=u,e[u].pb(v);
}
dfs1(0),dfs2(0);
read(m);
while(m--){
char opt;
scanf(" %c",&opt);
if(opt=='A'){
int u,v;
ll val;
read(u)read(v)read(val);
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
upd(1,1,n,dfn[top[u]],dfn[u],val);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
upd(1,1,n,dfn[u],dfn[v],val);
}
else{
int u;
read(u);
printf("%lld\n",query(1,1,n,dfn[u],dfn[u]+sz[u]-1));
}
}
return 0;
}
}
int main(){return asbt::main();}
H. [SHOI2016] 随机序列
简单手摸后发现,答案就是这么一个式子:
\((3^{n-1}-3^{n-2})a_1+(3^{n-2}-3^{n-3})a_1a_2+\dots+(3^1-3^0)a_1a_2\dots a_{n-1}+a_1a_2\dots a_n\)
简单预处理,修改时取逆元区间乘就行了。
但其实不行,因为输入的是非负数,而不是正整数,而 \(0\) 是没逆元的。
观察式子,发现从第一个 \(0\) 往后的项就都是 \(0\) 了。因此可以用一个 set
来维护 \(0\) 的位置。操作时正常操作,把 \(0\) 当成 \(1\) 就行了。查询时只查 \(1\) 到第一个 \(0\) 的位置前面。
Code>
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,mod=1e9+7;
int n,m,a[maxn],b[maxn],pw3[maxn];
int sum[maxn<<2],tag[maxn<<2];
il void pushup(int id){
sum[id]=(sum[lid]+sum[rid])%mod;
}
il void pushtag(int id,int val){
tag[id]=tag[id]*1ll*val%mod;
sum[id]=sum[id]*1ll*val%mod;
}
il void pushdown(int id){
if(tag[id]>1){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=1;
}
}
il void build(int id,int l,int r){
tag[id]=1;
if(l==r){
sum[id]=a[l];
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void upd(int id,int L,int R,int l,int r,int val){
if(L>=l&&R<=r){
pushtag(id,val);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,val);
}
if(r>mid){
upd(rid,mid+1,R,l,r,val);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return sum[id];
}
pushdown(id);
int mid=(L+R)>>1,res=0;
if(l<=mid){
(res+=query(lid,L,mid,l,r))%=mod;
}
if(r>mid){
(res+=query(rid,mid+1,R,l,r))%=mod;
}
return res;
}
il void debug(int id,int l,int r){
cout<<id<<" "<<l<<" "<<r<<" "<<sum[id]<<"\n";
if(l==r){
return ;
}
pushdown(id);
int mid=(l+r)>>1;
debug(lid,l,mid);
debug(rid,mid+1,r);
}
il int qpow(int x,int y){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
set<int> zero;
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m);
pw3[0]=1;
for(int i=1;i<=n;i++){
read(a[i]);
if(!a[i]){
a[i]=1;
zero.insert(i);
}
b[i]=a[i];
pw3[i]=pw3[i-1]*3ll%mod;
}
zero.insert(n+1);
for(int i=n;i;i--){
(pw3[i]+=mod-pw3[i-1])%=mod;
}
for(int i=2;i<=n;i++){
a[i]=a[i]*1ll*a[i-1]%mod;
}
for(int i=1;i<=n;i++){
a[i]=a[i]*1ll*pw3[n-i]%mod;
}
build(1,1,n);
while(m--){
int pos,val;
read(pos)read(val);
zero.erase(pos);
if(!val){
val=1,zero.insert(pos);
}
upd(1,1,n,pos,n,val*1ll*qpow(b[pos],mod-2)%mod);
b[pos]=val;
printf("%d\n",*zero.begin()==1?0:query(1,1,n,1,*zero.begin()-1));
}
return 0;
}
}
int main(){return asbt::main();}
/*
4 1
1 2 3 4
1 2
*/