线段树补充
线段树补充
**自己写的change一定要down!!!
wa了好几次了
线段树维护矩阵
很多线性的变换都可以用矩阵来处理,包括累加,旋转等操作,用矩阵可以维护很多次的加
矩阵之间的乘法可以有矩阵快速幂
本随笔中的矩阵乘法均为右乘,就是一个横着的需要变换的值去乘n*n的矩阵
目前为止应该没写什么优化 时间比较极限
写了5道比较模板的题目 还有一道题单中的还没写出来 后续会更新整理
如有错误 感谢指出!
矩阵模板
和普通快速幂同理
int M;
struct matrix {
ll x[M+1][M+1];
matrix() {
memset(x,0,sizeof(x));
}
};
matrix multiply(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
for(int k=1; k<=M; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j]%mod;
return c;
}
matrix addMat(matrix &a,matrix &b){
matrix c;
for(int i=1;i<=M;i++)
for(int j=1;j<=M;j++)
c.x[i][j]=(a.x[i][j]+b.x[i][j])%mod;
return c;
}
matrix mpow(matrix a,ll m) { //矩阵a的m次方
matrix res;
for(int i=1; i<=M; i++) res.x[i][i]=1; //单位矩阵
while(m>0) {
if(m&1) res=multiply(res,a);
a=multiply(a,a);
m>>=1;
}
return res;
}
void Debug(matrix &a) {
for(int i=1; i<=M; i++) {
for(int j=1; j<=M; j++) cout<<a.x[i][j]<<" ";
cout<<'\n';
}
}
bool check(matrix a){
int flag=true;
for(int i=1;i<=M;i++){
for(int j=1;j<=M;j++){
if(i!=j&&a.x[i][j]!=0) flag=false;
else if(i==j&&a.x[i][j]!=1) flag=false;
}
}
return flag;
}
对某个数的重复操作可以用矩阵乘法来维护
比如说 要把A,B中的A变成A+B, 就可以对
右乘一个
这个就是斐波那契数列的矩阵求法,A为fn B为fn-1 fn=fn-1+fn-2 fn-1=fn-1
可以由上一项推出
矩阵乘法是要有顺序的,在重复的情况下可以用快速幂加速
否则用线段树维护区间的加
题1 Addition Robot
对于这道题,要有顺序的处理A和B矩阵的乘法顺序
因此可以用线段树维护乘法,一直向右乘就行了
在放懒标记的时候,交换矩阵的对角
因为是改变A和B A和B矩阵的作用就在对角,交换一下就行了
看别人也有维护两个矩阵的
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 1e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
#define int ll
struct matrix {
ll x[3][3];
matrix() {
memset(x,0,sizeof(x));
}
};
matrix multiply(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=2; i++)
for(int j=1; j<=2; j++)
for(int k=1; k<=2; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j]%mod;
return c;
}
matrix mpow(matrix &a,ll m) { //矩阵a的m次方
matrix res;
for(int i=1; i<=2; i++) res.x[i][i]=1; //单位矩阵
while(m>0) {
if(m&1) res=multiply(res,a);
a=multiply(a,a);
m>>=1;
}
return res;
}
struct Info {
matrix x;
};
Info operator +(const Info &a,const Info &b) {
Info c;
matrix tmpa,tmpb;
tmpa=a.x;
tmpb=b.x;
c.x=multiply(tmpa,tmpb);
return c;
}
struct node {
int lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,int tag) {
seg[id].lazy^=1;
swap(seg[id].val.x.x[1][2],seg[id].val.x.x[2][1]);
swap(seg[id].val.x.x[2][2],seg[id].val.x.x[1][1]);
}
void down(int id,int l,int r) {
if(seg[id].lazy==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy=0;
}
char a[MAXN];
matrix A,B;
void build(int id,int l,int r) {
if(l==r) {
if(a[l]=='A')
seg[id].val.x=A;
else seg[id].val.x=B;
seg[id].lazy=0;
return;
}
int mid=l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,int val) {
if (ql<=l&&r<=qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,qr,val);
modify(id<<1|1,mid+1,r,ql,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql<=l&&r<=qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,qr)+query(id<<1|1,mid+1,r,ql,qr);
}
void solve() {
A.x[1][1]=1;
A.x[1][2]=0;
A.x[2][1]=1;
A.x[2][2]=1;
B.x[1][1]=1;
B.x[1][2]=1;
B.x[2][1]=0;
B.x[2][2]=1;
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
build(1,1,n);
while(m--) {
int op;
cin>>op;
if(op==2) {
int l,r;
cin>>l>>r;
int aa,bb;
cin>>aa>>bb;
matrix tmp;
tmp.x[1][1]=aa;
tmp.x[1][2]=bb;
matrix k;
k=query(1,1,n,l,r).x;
tmp=multiply(tmp,k);
cout<<tmp.x[1][1]%mod<<" "<<tmp.x[1][2]%mod<<'\n';
} else {
int l,r;
cin>>l>>r;
modify(1,1,n,l,r,1);
}
}
}
signed main() {
solve();
}
题2 Sasha and Array
维护斐波那契数列
乘上一个矩阵可以快速得出斐波那契数列
这个比较简单~
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
const ll M=2;
#define int ll
struct matrix {
ll x[M+1][M+1];
matrix() {
memset(x,0,sizeof(x));
}
};
matrix multiply(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
for(int k=1; k<=M; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j]%mod;
return c;
}
matrix mpow(matrix a,ll m) { //矩阵a的m次方
matrix res;
for(int i=1; i<=M; i++) res.x[i][i]=1; //单位矩阵
while(m>0) {
if(m&1) res=multiply(res,a);
a=multiply(a,a);
m>>=1;
}
return res;
}
void Debug(matrix &a) {
for(int i=1; i<=M; i++) {
for(int j=1; j<=M; j++) cout<<a.x[i][j]<<" ";
cout<<'\n';
}
}
struct Info {
matrix sum;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.sum.x[1][1]=(a.sum.x[1][1]+b.sum.x[1][1])%mod;
c.sum.x[1][2]=(a.sum.x[1][2]+b.sum.x[1][2])%mod;
c.sum.x[2][1]=(a.sum.x[2][1]+b.sum.x[2][1])%mod;
c.sum.x[2][2]=(a.sum.x[2][2]+b.sum.x[2][2])%mod;
return c;
}
matrix A;
int a[MAXN];
struct node {
int lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,int tag) {
matrix tmp=mpow(A,tag);
seg[id].val.sum=multiply(seg[id].val.sum,tmp);
seg[id].lazy+=tag;
}
void down(int id,int l,int r) {
if(seg[id].lazy==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy=0;
}
void build(int id,int l,int r) {
if(l==r) {
seg[id].val.sum.x[1][1]=1;
seg[id].val.sum.x[2][2]=1;
matrix tmp=mpow(A,a[l]-1);
seg[id].val.sum=multiply(seg[id].val.sum,tmp);
seg[id].lazy=0;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,int val) {
if (ql<=l&&r<=qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,qr,val);
modify(id<<1|1,mid+1,r,ql,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql<=l&&r<=qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,qr)+query(id<<1|1,mid+1,r,ql,qr);
}
void solve() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
A.x[1][1]=1;
A.x[1][2]=1;
A.x[2][1]=1;
A.x[2][2]=0;
build(1,1,n);
while(m--) {
int op;
cin>>op;
if(op==1) {
int l,r,x;
cin>>l>>r>>x;
modify(1,1,n,l,r,x);
} else {
int l,r;
cin>>l>>r;
matrix tmp =query(1,1,n,l,r).sum;
matrix ans;
ans.x[1][1]=1;
ans=multiply(ans,tmp);
cout<<ans.x[1][1]%mod<<'\n';
}
}
}
signed main() {
solve();
}
*这题时间卡的好死 感觉写再丑点就要t了
题3 Robot Arm
维护一个(x,y)的贡献矩阵 旋转很容易想到是矩阵的乘(旋转公式
逆时针: x=xcosa-ysina y=xsina+ycosa
顺时针: x=cosa+ysina y=-xsina+ycosa
这种东西是可以用矩阵的
拉长就是矩阵两个值按比例增加 很粗暴
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
const int M=2;
const double PI = acos(-1);
struct matrix {
double x[M+1][M+1];
matrix() {
memset(x,0,sizeof(x));
}
void init() {
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++) {
if(i==j) x[i][j]=1;
else x[i][j]=0;
}
}
};
void Debug(matrix &a) {
for(int i=1; i<=M; i++) {
for(int j=1; j<=M; j++) printf("%.6f ",a.x[i][j]);
cout<<'\n';
}
}
matrix multiply(matrix a,matrix b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
for(int k=1; k<=M; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j];
return c;
}
matrix addMat(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
c.x[i][j]=(a.x[i][j]+b.x[i][j]);
// Debug(c);
return c;
}
matrix mpow(matrix a,ll m) { //矩阵a的m次方
matrix res;
for(int i=1; i<=M; i++) res.x[i][i]=1; //单位矩阵
while(m>0) {
if(m&1) res=multiply(res,a);
a=multiply(a,a);
m>>=1;
}
return res;
}
struct Info {
matrix sum;
};
Info operator +(const Info &a,const Info &b) {
Info c;
matrix aa,bb;
aa=a.sum;
bb=b.sum;
c.sum=addMat(aa,bb);
return c;
}
struct node {
matrix lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val.sum=addMat(seg[id<<1].val.sum,seg[id<<1|1].val.sum);
matrix tmp = addMat(seg[id<<1].val.sum,seg[id<<1|1].val.sum);
}
void settag(int id,int l,int r,matrix tag) {
seg[id].val.sum=multiply(seg[id].val.sum,tag);
seg[id].lazy=multiply(seg[id].lazy,tag);
}
bool check(matrix a){
int flag=true;
for(int i=1;i<=M;i++){
for(int j=1;j<=M;j++){
if(i!=j&&a.x[i][j]!=0) flag=false;
else if(i==j&&a.x[i][j]!=1) flag=false;
}
}
return flag;
}
void down(int id,int l,int r) {
if(check(seg[id].lazy)) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy.init();
}
void build(int id,int l,int r) {
seg[id].lazy.init();
if(l==r) {
seg[id].val.sum.x[1][1]=1;
seg[id].val.sum.x[1][2]=0;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
matrix make(int val) {
matrix c;
c.x[1][1]=cos(val*1.0*PI/180);
c.x[1][2]=-sin(val*1.0*PI/180);
c.x[2][1]=sin(val*1.0*PI/180);
c.x[2][2]=cos(val*1.0*PI/180);
return c;
}
void modify(int id,int l,int r,int ql,int qr,matrix val) {
if (ql<=l&&r<=qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,qr,val);
modify(id<<1|1,mid+1,r,ql,qr,val);
}
up(id);
}
void change(int id,int l,int r,int pos,int val) {
if(r<pos||l>pos) return;
if(l==r&&l==pos) {
double len=seg[id].val.sum.x[1][1]*seg[id].val.sum.x[1][1]+seg[id].val.sum.x[1][2]*seg[id].val.sum.x[1][2];
len=sqrt(len);
seg[id].val.sum.x[1][1]=(len+val)/len*seg[id].val.sum.x[1][1];
seg[id].val.sum.x[1][2]=(len+val)/len*seg[id].val.sum.x[1][2];
return;
}
int mid=l+r>>1;
down(id,l,r);
change(id<<1,l,mid,pos,val);
change(id<<1|1,mid+1,r,pos,val);
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql<=l&&r<=qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,qr)+query(id<<1|1,mid+1,r,ql,qr);
}
void solve() {
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
for(int i=1; i<=m; i++) {
int op;
scanf("%d",&op);
if(op==1) {
int p,v;
cin>>p>>v;
change(1,1,n,p,v);
} else {
int p,v;
scanf("%d%d",&p,&v);
matrix tmp=make(v);
modify(1,1,n,p,n,tmp);
}
matrix tmp = query(1,1,n,1,n).sum;
printf("%.6f %.6f\n",tmp.x[1][1],tmp.x[1][2]);
}
}
signed main() {
solve();
}
题4 Summer Homework
这个直接算fi*ai,多算的斐波那契用逆矩阵减掉就行了
线段树维护每个点的fi*ai以及其和
比如说求a3~a5 我们已知f3xa3+f4xa4+f5xa5
我们需要求f1xa3+f2xa4+f3xa5
每一项都多乘了2个斐波那契的矩阵
乘回两个逆矩阵就行了 非常简单
这个好像取模不影响逆矩阵,很神奇,明明普通的除法会影响,矩阵除就不会了
*wa31是因为预处理斐波那契太少了
*wa40是因为change没有down
谨记
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define int ll
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 1e9;
const ll inf = 0x3f3f3f3f;
const int M=2;
ll a[MAXN];
struct matrix {
ll x[M+1][M+1];
matrix() {
memset(x,0,sizeof(x));
}
};
matrix multiply(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
for(int k=1; k<=M; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j]%mod,c.x[i][j]%=mod;
return c;
}
matrix addMat(matrix &a,matrix &b) {
matrix c;
for(int i=1; i<=M; i++)
for(int j=1; j<=M; j++)
c.x[i][j]=(a.x[i][j]+b.x[i][j])%mod;
return c;
}
matrix mpow(matrix a,ll m) { //矩阵a的m次方
matrix res;
for(int i=1; i<=M; i++) res.x[i][i]=1; //单位矩阵
while(m>0) {
if(m&1) res=multiply(res,a);
a=multiply(a,a);
m>>=1;
}
return res;
}
void Debug(matrix &a) {
for(int i=1; i<=M; i++) {
for(int j=1; j<=M; j++) cout<<a.x[i][j]<<" ";
cout<<'\n';
}
}
matrix A,B;
matrix f[MAXN];
struct Info {
matrix sum;//总和
matrix x;//矩阵
};
Info operator +(const Info &a,const Info &b) {
Info c;
matrix tmpa=a.sum;
matrix tmpb=b.sum;
c.sum=addMat(tmpa,tmpb);
tmpa=a.x;
tmpb=b.x;
c.x=addMat(tmpa,tmpb);
return c;
}
struct node {
ll lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r, int tag) {
seg[id].val.sum.x[1][1]+=(long long)tag*seg[id].val.x.x[1][1];
seg[id].val.sum.x[1][1]%=mod;
seg[id].val.sum.x[1][2]+=(long long)tag*seg[id].val.x.x[1][2];
seg[id].val.sum.x[1][2]%=mod;
seg[id].lazy+=tag;
seg[id].lazy%=mod;
}
void down(int id,int l,int r) {
if(seg[id].lazy==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy=0;
}
void build(int id,int l,int r) {
if(l==r) {
seg[id].val.sum.x[1][1]=a[l]*f[l].x[1][1];
seg[id].val.sum.x[1][1]%=mod;
seg[id].val.sum.x[1][2]=a[l]*f[l].x[1][2];
seg[id].val.sum.x[1][2]%=mod;
seg[id].val.x.x[1][1]=f[l].x[1][1];
seg[id].val.x.x[1][1]%=mod;
seg[id].val.x.x[1][2]=f[l].x[1][2];
seg[id].val.x.x[1][2]%=mod;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,int val) {
if (ql==l&&r==qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,mid,val);
modify(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
void change(int id,int l,int r,int pos,int val) {
if(l==r&&l==pos) {
seg[id].val.sum.x[1][1]=(long long)val*f[l].x[1][1];
seg[id].val.sum.x[1][1]%=mod;
seg[id].val.sum.x[1][2]=(long long)val*f[l].x[1][2];
seg[id].val.sum.x[1][2]%=mod;
return;
}
down(id,l,r);
int mid=l+r>>1;
if(mid>=pos) {
change(id<<1,l,mid,pos,val);
} else {
change(id<<1|1,mid+1,r,pos,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql==l&&r==qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,mid)+query(id<<1|1,mid+1,r,mid+1,qr);
}
void init() {
f[1].x[1][1]=1;
for(int i=2; i<=3e5; i++) {
f[i]=multiply(f[i-1],A);
}
}
void solve() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
build(1,1,n);
for(int i=1; i<=m; i++) {
int op;
cin>>op;
if(op==1) {
int p,v;
cin>>p>>v;
change(1,1,n,p,v);
} else if(op==2) {
int l,r;
cin>>l>>r;
matrix tmp = mpow(B,l-1);
matrix ans=query(1,1,n,l,r).sum;
ans=multiply(ans,tmp);
cout<<(ans.x[1][1]+mod)%mod<<'\n';
} else {
int l,r,d;
cin>>l>>r>>d;
modify(1,1,n,l,r,d);
}
}
}
signed main() {
close;
//{f[n],f[n-1]}
//{1,1}
//{1,0}
A.x[1][1]=1;
A.x[1][2]=1;
A.x[2][1]=1;//斐波那契
B.x[1][2]=1;
B.x[2][1]=1;
B.x[2][2]=-1;//逆矩阵
init();
solve();
}
题5 Paimon Segment Tree
https://codeforces.com/gym/103470/problem/E
区间平方和以及历史平方和是一个线性的递推关系 显然是可以用矩阵来维护的
历史和=历史和+当次和 来维护就行了
注意没有添加的也要更新历史和
历史和=历史和+上一次和
字有点丑 但是差不多这个意思
乘矩阵就行了
很痛苦的卡常,千万不要取多余的mod!!!
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 5e4+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
const ll M = 4;
inline ll read() { //快读
ll x=0,f=0;
char ch=getchar();
while(ch>'9'||ch<'0') {
f|=(ch=='-');
ch=getchar();
}
while(ch<='9'&&ch>='0') {
x=(x<<1LL)+(x<<3LL)+
(ch^48);
ch=getchar();
}
return f?-x:x;
}
struct matrix {
ll x[M+1][M+1];
matrix() {
memset(x,0,sizeof(x));
}
ll* operator [] (int i) {
return x[i];
}
matrix operator * (matrix b) const {
matrix res;
for(int i = 1; i <= M; i ++) {
for(int j = 1; j <= M; j ++) {
for(int k = i; k <= j; k ++) {
res[i][j] += (( x[i][k] *b[k][j]) );
res[i][j]%=mod;
}
}
}
return res;
}
matrix operator + ( matrix b) const {
matrix res;
for (int i = 1; i <=M; i ++) {
for (int j = i; j <= M; j ++) {
res[i][j] = (x[i][j] + b[i][j] );
res[i][j]%=mod;
}
}
return res;
}
bool operator != (matrix b) {
for (int i = 1; i <= M; i ++) {
for (int j = 1; j <=M; j ++) {
if (x[i][j] != b[i][j])return true;
}
}
return false;
}
void init() {
for(int i = 1 ; i <=M ; i ++) {
for(int j = i ; j <=M ; j ++) {
x[i][j] = (i == j ? 1LL : 0LL);
}
}
}
};
matrix mpow(matrix a,ll m) { //矩阵a的m次方
matrix res;
res.init();
while(m>0) {
if(m&1) res=res*a;
a=a*a;
m>>=1;
}
return res;
}
bool check(matrix a) {
int flag=true;
for(int i=1; i<=M; i++) {
for(int j=1; j<=M; j++) {
if(i!=j&&a.x[i][j]!=0) flag=false;
else if(i==j&&a.x[i][j]!=1) flag=false;
}
}
return flag;
}
struct Info {
matrix sum;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.sum=a.sum+b.sum;
return c;
}
matrix A;
ll a[MAXN];
struct node {
matrix lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,matrix tag) {
matrix tmp;
seg[id].val.sum=seg[id].val.sum*tag;
seg[id].lazy=seg[id].lazy*tag;
}
void down(int id,int l,int r) {
if(seg[id].lazy.x[3][4]==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy.init();
}
void build(int id,int l,int r) {
seg[id].lazy.init();
if(l==r) {
seg[id].val.sum[1][1]=1;
seg[id].val.sum[1][2]=a[l];
seg[id].val.sum[1][3]=a[l]*a[l]%mod;
seg[id].val.sum[1][4]=a[l]*a[l]%mod;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,matrix val) {
if (ql==l&&r==qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,mid,val);
modify(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql==l&&r==qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,mid)+query(id<<1|1,mid+1,r,mid+1,qr);
}
vector<array<int,3> > P;
vector<array<int,4> > Q[MAXN];//id l r ty
ll ans[MAXN];
void solve() {
int n,m,q;
// cin>>n>>m>>q;
n=read();
m=read();
q=read();
for(int i=1; i<=n; i++) a[i]=read();
build(1,1,n);
P.push_back({0,0,0});
for(int i=1; i<=m; i++) {
int l,r,x;
// cin>>l>>r>>x;
l=read();
r=read();
x=read();
P.push_back({l,r,x});
}
for(int i=1; i<=q; i++) {
int l,r,x,y;
// cin>>l>>r>>x>>y;
l=read();
r=read();
x=read();
y=read();
if(x!=0)
Q[x-1].push_back({i,l,r,-1});
Q[y].push_back({i,l,r,1});
}
for(auto it:Q[0]) {
int id=it[0],l=it[1],r=it[2],ty=it[3];
matrix tmp = query(1,1,n,l,r).sum;
ans[id]+=ty*tmp[1][4];
}
for(int i=1; i<=m; i++) {
int l=P[i][0],r=P[i][1],x=P[i][2];
matrix tmp;
tmp[1][1]=1;
tmp[1][2]=x;
tmp[1][3]=1LL*x*x%mod;
tmp[1][4]=1LL*x*x%mod;
tmp[2][2]=1;
tmp[2][3]=2LL*x;
tmp[2][4]=2LL*x;
tmp[3][3]=1;
tmp[3][4]=1;
tmp[4][4]=1;
modify(1,1,n,l,r,tmp);
tmp.init();
tmp[3][4]=1;
if(l>1) {
modify(1,1,n,1,l-1,tmp);
}
if(r<n) {
modify(1,1,n,r+1,n,tmp);
}
for(auto it:Q[i]) {
int id=it[0],l=it[1],r=it[2],ty=it[3];
matrix tmp = query(1,1,n,l,r).sum;
ans[id]+=ty*tmp[1][4];
}
}
for(int i=1; i<=q; i++) {
printf("%lld\n",(ans[i]%mod+mod)%mod);
}
}
signed main() {
solve();
}
题6 大秦酒店欢迎您
Dashboard - 2023 Xian Jiaotong University Programming Contest - Codeforces
这题场上想写莫队但是来不及了
卡空间还卡时间 不过可能是我矩阵太丑了orz
和题5 很相似 维护(len,sum ssum) 就可以用矩阵乘来算
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 5e5+7;
const ll mod = 4294967296;
const ll inf = 0x3f3f3f3f;
vector<array<int,2> > q[MAXN];
int pre[MAXN];
int a[MAXN];
ll ans[MAXN];
const int M = 3;
struct matrix {
ll x[M][M];
matrix() {
memset(x,0,sizeof(x));
}
void init() {
for(int i=0; i<M; i++) {
for(int j=i; j<M; j++) {
if(i==j) x[i][j]=1;
else x[i][j]=0;
}
}
}
};
matrix multiply(matrix &a,matrix &b) {
matrix c;
for(int i=0; i<M; i++)
for(int j=0; j<M; j++)
for(int k=i; k<=j; k++)
c.x[i][j]+=a.x[i][k]*b.x[k][j]%mod;
return c;
}
matrix addMat(matrix &a,matrix &b) {
matrix c;
for(int i=0; i<M; i++)
for(int j=i; j<M; j++)
c.x[i][j]=(a.x[i][j]+b.x[i][j])%mod;
return c;
}
struct Info {
matrix sum;
};
Info operator +(const Info &a,const Info &b) {
Info c;
matrix aa=a.sum,bb=b.sum;
c.sum=addMat(aa,bb);
return c;
}
struct node {
matrix lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,matrix tag) {
seg[id].val.sum=multiply(seg[id].val.sum,tag);
seg[id].lazy=multiply(seg[id].lazy,tag);
}
void down(int id,int l,int r) {
if(seg[id].lazy.x[1][2]==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy.x[0][1]=0;
seg[id].lazy.x[0][2]=0;
seg[id].lazy.x[1][2]=0;
}
void build(int id,int l,int r) {
seg[id].lazy.x[0][0]=1;
seg[id].lazy.x[1][1]=1;
seg[id].lazy.x[2][2]=1;
if(l==r) {
seg[id].val.sum.x[0][0]=1;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,matrix val) {
if (ql==l&&r==qr) {
settag(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,mid,val);
modify(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql==l&&r==qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,mid)+query(id<<1|1,mid+1,r,mid+1,qr);
}
void solve() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
build(1,1,n);
for(int i=1; i<=m; i++) {
int l,r;
cin>>l>>r;
q[r].push_back({l,i});
}
for(int i=1; i<=n; i++) {
matrix t1,t2;
t1.x[0][0]=1;
t1.x[1][2]=1;
t1.x[1][1]=1;
t1.x[2][2]=1;
t2.x[0][1]=1;
t2.x[0][2]=1;
t2.x[0][0]=1;
t2.x[1][2]=1;
t2.x[1][1]=1;
t2.x[2][2]=1;
modify(1,1,n,pre[a[i]]+1,i,t2);
if(pre[a[i]]>=1)
modify(1,1,n,1,pre[a[i]],t1);
for(auto &it:q[i]) {
ans[it[1]]=query(1,1,n,it[0],i).sum.x[0][2];
}
pre[a[i]]=i;
}
for(int i=1; i<=m; i++) {
cout<<ans[i]<<"\n";
}
}
signed main() {
close;
solve();
}
势能线段树
势能线段树支持一些暴力的改法,本身保证次数不多所以可以暴力修改
在题目的思考过程中 不能被显然t的做法吓跑
应该冷静分析次数 找到势能上可以均摊复杂度的证明方法
(这部分感谢实验室学长的debug以及优化方法
题1 Lowbit
写的很丑所以又卡时间了orz
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 998244353;
const ll inf = 0x3f3f3f3f;
#define int ll
int a[MAXN];
int lowbit(int x) {
return x&-x;
}
int bitcnt(int x) {
int cnt=0;
while(x) {
x-=lowbit(x);
cnt++;
}
return cnt;
}
struct Info {
int sum,cnt;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.sum=a.sum+b.sum;
c.sum%=mod;
c.cnt=a.cnt+b.cnt;
return c;
}
struct node {
int lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,int val) {
seg[id].val.sum*=val;
seg[id].val.sum%=mod;
seg[id].lazy*=val;
seg[id].lazy%=mod;
}
void down(int id,int l,int r) {
if(seg[id].lazy==1) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy=1;
}
void build(int id,int l,int r) {
seg[id].lazy=1;
if(l==r) {
seg[id].val.sum=a[l];
seg[id].val.cnt=bitcnt(a[l]);
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void change(int id,int l,int r,int pos) {
if(r<pos||l>pos) return;
if(l==r&&l==pos) {
int k=lowbit(seg[id].val.sum);
if(seg[id].val.cnt==1)
seg[id].val.sum*=2,seg[id].val.sum%=mod;
else seg[id].val.sum+=k,seg[id].val.cnt=bitcnt(seg[id].val.sum);
return;
}
int mid=l+r>>1;
down(id,l,r);
change(id<<1,l,mid,pos);
change(id<<1|1,mid+1,r,pos);
up(id);
}
void modify(int id,int l,int r,int ql,int qr) {
if (ql<=l&&r<=qr) {
if(seg[id].val.cnt==r-l+1)
settag(id,l,r,2);
else{
for(int i=l;i<=r;i++) change(id,l,r,i);
}
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr);
else {
modify(id<<1,l,mid,ql,qr);
modify(id<<1|1,mid+1,r,ql,qr);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql<=l&&r<=qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,qr)+query(id<<1|1,mid+1,r,ql,qr);
}
void solve() {
int n;
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i];
build(1,1,n);
// cout<<"# \n";
// for(int i=1;i<=n;i++) cout<<query(1,1,n,i,i).sum<<" "<<query(1,1,n,i,i).cnt<<"\n";
int q;
cin>>q;
while(q--) {
int op,l,r;
cin>>op>>l>>r;
if(op==1) {
modify(1,1,n,l,r);
} else {
cout<<query(1,1,n,l,r).sum%mod<<"\n";
}
}
}
signed main() {
close;
int t;
cin>>t;
while(t--)
solve();
}
题2 花神游历各国
P4145 上帝造题的七分钟 2 / 花神游历各国 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
与上题不同 本题主要是由于一个数可以被开方的次数有限
并且对于1 都是无限开方,因此 暴力开方就行了
采用了比较快地写法
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
#define int ll
int a[MAXN];
struct Info {
int sum;
int flag;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.sum=a.sum+b.sum;
c.flag=(a.flag&b.flag);
return c;
}
struct node {
int lazy;
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag(int id,int l,int r,int tag) {
;
}
void down(int id,int l,int r) {
if(seg[id].lazy==0) return;
int mid=l+r>>1;
settag(id<<1,l,mid,seg[id].lazy);
settag(id<<1|1,mid+1,r,seg[id].lazy);
seg[id].lazy=0;
}
void build(int id,int l,int r) {
if(l==r) {
seg[id].val.sum=a[l];
if(a[l]==1) {
seg[id].val.flag=1;
}
seg[id].lazy=0;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,int val) {
if (ql==l&&r==qr) {
if(seg[id].val.flag==1) {
settag(id,l,r,val);
return;
}
}
if(l==r){
seg[id].val.sum=sqrt(seg[id].val.sum);
if(seg[id].val.sum==1) seg[id].val.flag=1;
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify(id<<1|1, mid+1,r,ql,qr,val);
else {
modify(id<<1,l,mid,ql,mid,val);
modify(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql==l&&r==qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,mid)+query(id<<1|1,mid+1,r,mid+1,qr);
}
void solve() {
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int q;cin>>q;
build(1,1,n);
while(q--){
int op,l,r;cin>>op>>l>>r;
if(l>r) swap(l,r);
if(op==0){
modify(1,1,n,l,r,1);
}
else{
cout<<query(1,1,n,l,r).sum<<"\n";
}
}
}
signed main() {
solve();
}
题3 Innovations
差做法 树剖+势能
写的时候线段树板子错了
#include<bits/stdc++.h>
#define QAQ 0
#define close \
std::ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
typedef long long ll;
const int MAXN = 2e5 + 7;
const int mod = 1e9+7;
using namespace std;
ll a[MAXN];
vector<pair<int,ll> > adj[MAXN];
int deep[MAXN],siz[MAXN],hson[MAXN],rak[MAXN],dfn[MAXN],f[MAXN],cnt=0,top[MAXN];
ll ans=0,sum;
void tree_build(int u,int fa,int dep) {
deep[u]=dep;
siz[u]=1;
f[u]=fa;
for(auto &it:adj[u]) {
int v=it.first;
if(v==fa) continue;
ll w=it.second;
a[v]=w;
tree_build(v,u,dep+1);
siz[u]+=siz[v];
if(hson[u]==0||siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void tree_decom(int u,int t) {
top[u]=t;
cnt++;
dfn[u]=cnt;
rak[cnt]=u;
if(hson[u]!=0) {
tree_decom(hson[u],t);
for(auto &it:adj[u]) {
int v=it.first;
if(hson[u]!=v&&v!=f[u]) tree_decom(v,v);
}
}
}
struct Info {
ll sum;
bool flag;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.flag=(a.flag&b.flag);
return c;
}
struct node {
Info val;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void build(int id,int l,int r) {
if(l==r) {
seg[id].val.sum=a[rak[l]];
if(a[rak[l]]==1) seg[id].val.flag=1;
return;
}
int mid=l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify(int id,int l,int r,int ql,int qr,int val) {
if(seg[id].val.flag==1) return;
if(ql==l&&r==qr) {
if(seg[id].val.flag==1) {
return;
}
}
if(l==r) {
ll tmp=sqrt(seg[id].val.sum);
ll temp=seg[id].val.sum-tmp;
int sizz=siz[rak[l]];
ans-=(temp)*(sizz)*(sum-sizz);
ans%=mod;
if(ans<0) ans+=mod;
seg[id].val.sum=tmp;
if(tmp==1) seg[id].val.flag=1;
return;
}
int mid=(l+r)>>1;
if(qr<=mid) modify(id<<1,l,mid,ql,qr,val);
else if(ql>mid) modify(id<<1|1,mid+1,r,ql,qr,val);
else {
if(seg[id<<1].val.flag!=1)
modify(id<<1,l,mid,ql,mid,val);
if(seg[id<<1|1].val.flag!=1)
modify(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
void dfs(int u,int fa) {
for(auto &it:adj[u]) {
int v=it.first;
if(v==fa) continue;
ans+=a[v]*(siz[v])*(sum-siz[v]);
if(ans>mod)
ans%=mod;
dfs(v,u);
}
}
void solve() {
int n,q;
scanf("%d %d",&n,&q);
sum=n;
for(int i=1; i<n; i++) {
int u,v;
ll w;
scanf("%d %d %lld",&u,&v,&w);
adj[u].push_back({v,w});
adj[v].push_back({u,w});
}
tree_build(1,0,0);
tree_decom(1,1);
dfs(1,0);
build(1,1,n);
printf("%lld\n",ans);
for(int i=1; i<=q; i++) {
int u,v;
scanf("%d %d",&u,&v);
if(u==v||seg[1].val.flag==1) {
printf("%lld\n",ans);
continue;
}
while(top[u]!=top[v]) {
if(deep[top[u]]>deep[top[v]]) {
modify(1,1,n,dfn[top[u]],dfn[u],1);
u=f[top[u]];
} else {
modify(1,1,n,dfn[top[v]],dfn[v],1);
v=f[top[v]];
}
}
if(dfn[v]!=dfn[u]) {
if(dfn[v]<dfn[u]) swap(u,v);
modify(1,1,n,dfn[u]+1,dfn[v],1);
}
printf("%lld\n",ans);
}
}
signed main() {
solve();
return QAQ;
}
题4 Easy problem I
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN = 3e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
#define int ll
int a[MAXN],m, n;
struct Info {
int sum1,sum2;
int mn;
int len;
};
Info operator +(const Info &a,const Info &b) {
Info c;
c.sum1=a.sum1+b.sum1;
c.sum2=a.sum2+b.sum2;
c.len=a.len+b.len;
c.mn=min(a.mn,b.mn);
return c;
}
struct node {
Info val;
int tag1,tag2;
int mul;
} seg[MAXN<<2];
void up(int id) {
seg[id].val=seg[id<<1].val+seg[id<<1|1].val;
}
void settag_mul(int id,int l,int r,int mul) {
seg[id].val.sum2*=mul;
seg[id].mul*=mul;
seg[id].tag2*=mul;
}
void settag_tag2(int id,int l,int r,int tag2) {
seg[id].val.sum2+=tag2*(r-l+1-seg[id].val.len);
seg[id].tag2+=tag2;
}
void settag_tag1(int id,int l,int r,int tag1) {
seg[id].val.sum1+=tag1*(seg[id].val.len);
seg[id].tag1+=tag1;
seg[id].val.mn+=tag1;
}
void down(int id,int l,int r) {
// cout<<"TEST :"<<id<<" "<<seg[id].mul<<" "<<seg[id].tag2<<" "<<seg[id].tag1<<"\n";
int mid=l+r>>1;
if(seg[id].mul!=1) {
settag_mul(id<<1,l,mid,seg[id].mul);
settag_mul(id<<1|1,mid+1,r,seg[id].mul);
seg[id].mul=1;
}
if(seg[id].tag2!=0) {
settag_tag2(id<<1,l,mid,seg[id].tag2);
settag_tag2(id<<1|1,mid+1,r,seg[id].tag2);
seg[id].tag2=0;
}
if(seg[id].tag1!=0) {
settag_tag1(id<<1,l,mid,seg[id].tag1);
settag_tag1(id<<1|1,mid+1,r,seg[id].tag1);
seg[id].tag1=0;
}
}
void build(int id,int l,int r) {
seg[id].mul=1;
seg[id].tag1=0;
seg[id].tag2=0;
if(l==r) {
seg[id].val.sum1=a[l];
seg[id].val.len=1;
seg[id].val.mn=a[l];
seg[id].val.sum2=0;
return;
}
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
up(id);
}
void modify1(int id,int l,int r,int ql,int qr,int val) {
if(ql==l&&qr==r&&seg[id].val.mn>=val) {
settag_tag1(id,l,r,val*-1);
return;
}
if(l==r) {
seg[id].val.len=0;
seg[id].val.sum2=val-seg[id].val.sum1;
seg[id].val.sum1=0;
seg[id].val.mn=inf;
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify1(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify1(id<<1|1, mid+1,r,ql,qr,val);
else {
modify1(id<<1,l,mid,ql,mid,val);
modify1(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
void modify2(int id,int l,int r,int ql,int qr,int val) {
if(ql==l&&qr==r) {
settag_mul(id,l,r,-1);
settag_tag2(id,l,r,val);
return;
}
down(id,l,r);
int mid =(l+r) >> 1;
if (qr<=mid)
modify2(id <<1,l,mid,ql,qr,val);
else if (ql>mid)
modify2(id<<1|1, mid+1,r,ql,qr,val);
else {
modify2(id<<1,l,mid,ql,mid,val);
modify2(id<<1|1,mid+1,r,mid+1,qr,val);
}
up(id);
}
Info query(int id,int l ,int r,int ql,int qr) {
if(ql==l&&r==qr) {
return seg[id].val;
}
down(id,l,r);
int mid=l+r>>1;
if(qr<=mid) return query(id<<1,l,mid,ql,qr);
else if(ql>mid) return query(id<<1|1,mid+1,r,ql,qr);
else return query(id<<1,l,mid,ql,mid)+query(id<<1|1,mid+1,r,mid+1,qr);
}
void solve() {
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
build(1,1,n);
for(int i=1; i<=m; i++) {
int op;
cin>>op;
if(op==1) {
int l,r,x;
cin>>l>>r>>x;
modify2(1,1,n,l,r,x);
modify1(1,1,n,l,r,x);
} else {
int l,r;
cin>>l>>r;
cout<<query(1,1,n,l,r).sum1+query(1,1,n,l,r).sum2<<"\n";
}
}
}
signed main() {
close;
int t;
cin>>t;
while(t--)
solve();
}
做法2
被操作的点用并查集合并 一直往上跳直到lca 总复杂度不超过nlogn
扫描线
struct Segment{
ll l,r,h;
int val;
bool operator <(const Segment &k) const{
return h<k.h;
}
}seg[MAXN<<2];
离散化
int N;
int getX(int x){
int k=lower_bound(X.begin(),X.end(),x)-X.begin();
return k;
}
X.push_back(inf*-1);
sort(X.begin(),X.end());
X.erase(unique(X.begin(),X.end()),X.end());
N=X.size()-1;
矩形面积和
自下而上扫描,用线段树维护每次扫描时的线段长,扫描后进行处理
vector<int> X;
int N;
struct Info{
int sum,len;
};
struct Segment{
ll l,r,h;
int val;
Segment(ll a,ll b,ll c,int d){
l=a,r=b,h=c,val=d;
}
Segment(){
;
}
}Node[MAXN<<2];
bool bj(Segment a,Segment b){
return a.h<b.h;
}
Info operator +(const Info &a,const Info &b){
Info c;
c.len=a.len+b.len;
return c;
};
struct node{
int l,r,h;
Info val,lazy;
}seg[MAXN<<2];
void up(int id){
int l=seg[id].l,r=seg[id].r;
if(seg[id].val.sum)//不为0就加上
seg[id].val.len=X[r+1]-X[l];
else if(l<r){
seg[id].val.len=seg[id<<1].val.len+seg[id<<1|1].val.len;
}
else seg[id].val.len=0;
}
void change(int id,int lp,int rp,int val){//区间修改
int l=seg[id].l,r=seg[id].r;
if(lp==l&&r==rp){
seg[id].val.sum+=val;
up(id);
return;
}
int mid=(l+r)>>1;
if(rp<=mid){
change(id<<1,lp,rp,val);
}
else if(lp<=mid){
change(id<<1,lp,mid,val);
change(id<<1|1,mid+1,rp,val);
}
else{
change(id<<1|1,lp,rp,val);
}
up(id);
}
int getX(int x){
int k=lower_bound(X.begin(),X.end(),x)-X.begin();
return k;
}
void solve(){
int n;cin>>n;
for(int i=1;i<=n;i++){
int x1,x2,y1,y2;cin>>x1>>y1>>x2>>y2;
Node[i*2-1]=Segment(x1,x2,y1,1);
Node[i*2]=Segment(x1,x2,y2,-1);
X.push_back(x1);
X.push_back(x2);
}
X.push_back(inf*-1);
sort(Node+1,Node+2*n+1,bj);
sort(X.begin(),X.end());
X.erase(unique(X.begin(),X.end()),X.end());
N=X.size();
int ans=0;
build(1,1,N-2);
for(int i=1;i<n*2;i++){
int l=getX(Node[i].l);
int r=getX(Node[i].r);
change(1,l,r-1,Node[i].val);
ans+=seg[1].val.len*(Node[i+1].h-Node[i].h);
}
cout<<ans<<"\n";
}
题1 Equal Mod Segments
-
题目很短 意思就是问有多少个区间满足从左模到右和从右模到左相等
对于一个数字 mod上一个数 要不会让它的值减去大于等于它的1/2的数 要不不变
因此 一个数如果连续模上很多数 它的值的变化呈阶梯式 并且下降的次数一定会小于对于log2n
我们可以用二分的方式得到一个数往右走的所有不变的区间(阶梯是横线状态的区间) 记为i的区间为Li,Ri
从右往左也是如此 对于答案区间 我们设取的左端点和右端点为i j,那么右端点j肯定在某个Li Ri里 且左端点i在某个Lj Rj里 整理一下就是 寻找 Lj<i<Rj && Li<j<Ri的ij对数量
这是一个扫描线的经典问题 按i排序 LiRi看成横的线 查询区间看成竖的查询区间 就可以得出答案
对于上面二分的操作 二分里面用数据结构来区间查询最小值二分 保证复杂度为logn^2
又是一道赛场上写不出的题orz
#define int ll int a[MAXN]; vector<array<int,4> > event[MAXN]; int n; int t[MAXN<<1],h[MAXN<<1]; vector<array<int,2> > op; vector<int> allval; int lowbit(int x) { return x&-x; } ll getsum(int x) { ll sum=0; while(x) { sum+=t[x]; x-=lowbit(x); } return sum; } void addv(int x,int val) { while(x<=MAXN) { t[x]+=val; x+=lowbit(x); } } //以下为树状数组求最值 比较懒 偷的 void updata(int x, int k) { while (x <= n) { h[x] = k; int low = lowbit(x); for (int i = 1; i < low; i <<= 1) h[x] = min(h[x], h[x - i]); x += lowbit(x); } } int query(int x, int y) { int ans = inf; while (y >= x) { ans = min(a[y], ans), y -= 1; for (; y-lowbit(y) >= x; y -= lowbit(y)) ans = min(h[y], ans); } return ans; } //以下为二分 应该是一个左偏一个右偏吧 int findR(int L,int val) { int l=L,r=n; while(l<=r){ int mid=l+r>>1; if(query(L+1,mid)>val) l=mid+1; else r=mid-1; } return r; } int findL(int R,int val) { int l=1,r=R; while(l<=r){ int mid =l+r>>1; if(query(mid,R-1)>val) r=mid-1; else l=mid+1; } return l; } void solve() { cin>>n; for(int i=1;i<2*MAXN;i++) h[i]=inf; for(int i=1; i<=n; i++) cin>>a[i],updata(i,a[i]); a[n+1]=1; a[0]=1; //以下为求出所有区间的长度 for(int i=1; i<=n; i++) { int now=i; int val=a[now]; while(now<=n) { int r=findR(now,val); event[val].push_back({now,0,i,0}); event[val].push_back({r,2,i,0}); // cout<<i<<" "<<now<<" "<<r<<" "<<val<<"\n"; val%=a[r+1]; now=r+1; allval.push_back(val); } } for(int i=n; i>=1; i--) { int now=i; int val=a[now]; while(now>=1) { int l=findL(now,val); event[val].push_back({i,1,l,now}); // cout<<i<<' '<<now<<" "<<l<<" "<<val<<"\n"; allval.push_back(val); val%=a[l-1]; now=l-1; } } sort(allval.begin(),allval.end()); allval.erase(unique(allval.begin(),allval.end()),allval.end()); int ans=0; //以下为扫描线 for(auto &val:allval) { sort(event[val].begin(),event[val].end()); int tmp=0; op.clear(); for(auto &it:event[val]) { int pos=it[0]; int ty=it[1]; int l=it[2]; int r=it[3]; // cout<<val<<":"<<pos<<" "<<ty<<" "<<l<<" "<<r<<"\n"; if(ty==0) { addv(l,1); op.push_back({l,1}); } else if(ty==2) { addv(l,-1); op.push_back({l,-1}); } else { ans+=getsum(r)-getsum(l-1); tmp+=getsum(r)-getsum(l-1); } } for(auto &it:op) { int pos=it[0],p=it[1]; addv(pos,p*-1); } // cout<<val<<":"<<tmp<<"\n"; } cout<<ans; }
可持久化线段树
前置知识 权值线段树 查询起点区间内第k小 可以离线
表示在l-r区间的值的数出现了几个
在线段树上二分 求第k小的数字
(扫描线)
线段树上二分查询 或者使用树状数组上倍增查询
int serch(int id,int k){
int l=seg[id].l,r=seg[id].r;
if(l==r) {
if(seg[i].val.sum==0) return -1;
return l;
}
if(seg[i<<1].val.sum>=k) return serch(id<<1,k);
else return serch(id<<1,k-seg[i<<1].val.sum);
}
动态开点
使用哪些节点 就生成根节点的链
可持久化操作 记录不同的时间
其他操作均类似 需要id=++tot
void change(int &id,int l,int r,int pos,int val){
if(!id) id=++tot;
if(l==r){
seg[id]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) change(ls[id],l,mid,pos,val);
else change(rs[id],mid+1,r,pos,val);
up(id);
}
change
int tot=0,rs[MAXN],ls[MAXN],seg[MAXN],rt[MAXN];
void build(int &id,int l,int r){
id=+tot;
if(l==r){
seg[id]=a[l];
return;
}
int mid=l+r>>1;
build(ls[id],l,mid);
build(rs[id],mid+1,r);
}
//以上为动态开点
//插入一条路径
void up(int id){
seg[id]=seg[ls[id]]+seg[rs[id]];
}
void insert(int &id,int now,int l,int r,int pos,int val){
//now为版本
id=++tot;
seg[id]=seg[now];
ls[id]=ls[now];
rs[id]=rs[now];
if(l==r){
seg[id]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) insert(ls[id],ls[now],l,mid,pos,val);
else insert(rs[id],rs[now],mid+1,r,pos,val);
up(id);
}
//insert(rt[i],1,MAXN,x,val,rt[i-1]);
int query(int id,int now,int l,int r,int k){
if(l==r) return l;
int cnt=seg[ls[id]]-seg[ls[now]];//两个版本的树相减 找到区间的答案
int mid=l+r>>1;
if(cnt>=k) return query(ls[id],ls[now],l,mid,k); //两棵树一起向左走
else return query(rs[id],rs[now],mid+1,r,k-cnt);//两棵树一起向右走
}
//query(rt[r],rt[l-1],1,MAXN,k);
树上路径第k大
建 1-i路径的树
建每个点的树 从上个树公用点
在四颗树上做加减 就可以知道路径第k大
u+v-lca(u,v)-f[lca(u,v)]
珂朵莉树
(ODT)
用于一整个区间是一样的数字时
在修改和查询的时候非常暴力 类似分块
如果要操作的区间已经有了,就直接改这个区间
否则把最近的区间拆起来 或者合并 来达到新区间
在随机数据均摊的情况下复杂度为O(nloglogn) 不会证明
拆分模板
毛的
#define IT set<node>::iterator
int ans=0;
struct node
{
int l,r;
mutable ll v;
node(int L, int R=-1, ll V=0):l(L), r(R), v(V) {}
bool operator<(const node& o) const
{
return l < o.l;
}
};
set<node> ODT;
IT split(int pos){//拆分区间
IT it = ODT.lower_bound(node(pos));
if(it!=ODT.end()&&it->l==pos) return it;
--it;
int L = it->l,R = it->r;
ll V=it->v;
ODT.erase(it);
ODT.insert(node(L,pos-1,V));
return ODT.insert(node(pos,R,V)).first;
}
void assign_val(int l,int r, ll val){//区间赋值
int tot=0,len=0;
IT itr=split(r+1),itl=split(l);
ODT.erase(itl,itr);
ODT.insert(node(l,r,val));
}
题1 Physical Education Lessons
-
在修改的时候直接统计就行了
void solve(){ int n;cin>>n; ans=n; int q;cin>>q; ODT.insert(node(1,n,1)); while(q--){ int l,r,op; cin>>l>>r>>op; if(op==1){ assign_val(l,r,0); } else{ assign_val(l,r,1); } cout<<ans<<'\n'; } }
其他如上
线段树合并
开80-120倍
合并
int merge(int a,int b,int l,int r){
if(!a) return b;
if(!b) return a;
if(l==r){
//合并
return a;
}
int mid=l+r>>1;
tr[a].l=merge(tr[a].l,re[b].l,l,mid);
tr[a].r=merge(tr[a].r,re[b].r,mid+1,r);
up(a);
return a;
}
题1 Lomsat gelral
还是学dsu on tree时候的题
就是求每个点的子树出现次数最大的颜色编号的和
在线段树合并 思路就是对每个点建一颗权值线段树,维护出现的最大值以及次数 以及答案
然后从下到上合并每个树节点上的线段树
当然线段树是动态开点,不然会mle
合并的过程中 如果两个点均有值 就把这个值的数量加起来
比如子树a中 3有1个 子树b中 3有2个 那么他们合成的树中 3肯定是3个
再往上push_up一下就更新答案了
再合并后 对当前答案进行记录
int cnt=0;
int mx[MAXN<<2],ls[MAXN<<2],rs[MAXN<<2],ans[MAXN<<2],res[MAXN],a[MAXN],sum[MAXN<<2];
vector<int> adj[MAXN];
void up(int now) {
if(sum[ls[now]]<sum[rs[now]]) {
mx[now]=mx[rs[now]];
sum[now]=sum[rs[now]];
ans[now]=ans[rs[now]];
} else if(sum[ls[now]]>sum[rs[now]]) {
mx[now]=mx[ls[now]];
sum[now]=sum[ls[now]];
ans[now]=ans[ls[now]];
} else {
mx[now]=mx[ls[now]];
sum[now]=sum[ls[now]];
ans[now]=ans[ls[now]]+ans[rs[now]];
}
}
void update(int &now,int l,int r,int pos,int val) {
if(!now) now=++cnt;
if(l==r) {
mx[now]=l;
ans[now]=l;
sum[now]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(ls[now],l,mid,pos,val);
else update(rs[now],mid+1,r,pos,val);
up(now);
}
int merge(int a,int b,int l,int r) {
if(!a) return b;
if(!b) return a;
if(l==r) {
mx[a]=l;
ans[a]=l;
sum[a]+=sum[b];
return a;
}
int mid=l+r>>1;
ls[a]=merge(ls[a],ls[b],l,mid);
rs[a]=merge(rs[a],rs[b],mid+1,r);
up(a);
return a;
}
void dfs(int now,int fa) {
for(auto &v:adj[now]) {
if(v==fa) continue;
dfs(v,now);
merge(now,v,1,MAXN-5);
}
update(now,1,MAXN-5,a[now],1);
res[now]=ans[now];
}
void solve() {
int n;
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i],cnt++;
for(int i=1; i<n; i++) {
int u,v;
cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs(1,0);
for(int i=1; i<=n; i++) cout<<res[i]<<' ';
}
signed main() {
solve();
}
注:tle了请把数组开大
题2 雨天的尾巴
好像是经典题目
[P4556 Vani有约会] 雨天的尾巴 /【模板】线段树合并 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
vector<int> adj[MAXN]; int n,m;
int siz[MAXN],f[MAXN],hson[MAXN],deep[MAXN],top[MAXN],dfn[MAXN],rdfn[MAXN],rak[MAXN],cnt;
int tot=0;
int ans[MAXN<<2],ls[MAXN<<2],rs[MAXN<<2],sum[MAXN<<2];
int rt[MAXN];
int res[MAXN];
void tree_build(int u,int fa) {//重链优先搜索
siz[u]=1;
f[u]=fa;
hson[u]=0;
for(auto &v:adj[u]) {
if(v==fa) continue;
deep[v]=deep[u]+1;
tree_build(v,u);
siz[u]+=siz[v];
if(hson[u]==0||siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void tree_decom(int u,int t) {//dfn序
top[u]=t;
cnt++;
dfn[u]=cnt;
rak[cnt]=u;
if(hson[u]!=0) {
tree_decom(hson[u],t);
for(auto &v:adj[u]) {
if(hson[u]!=v&&v!=f[u]) tree_decom(v,v);
}
}
rdfn[u]=cnt;
}
int getLCA(int u,int v) {
while(top[v]!=top[u]) {
if(deep[top[u]]>deep[top[v]]) u=f[top[u]];
else v=f[top[v]];
}
return deep[u]>deep[v]?v:u;
}
void up(int now){
if(sum[ls[now]]<sum[rs[now]]){
ans[now]=ans[rs[now]];
sum[now]=sum[rs[now]];
}
else{
ans[now]=ans[ls[now]];
sum[now]=sum[ls[now]];
}
}
void update(int &now,int l,int r,int pos,int val){
if(!now) {
now=++tot;
}
if(l==r){
ans[now]=l;
sum[now]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(ls[now],l,mid,pos,val);
else update(rs[now],mid+1,r,pos,val);
up(now);
}
int merge(int a,int b,int l,int r){
if(!a) return b;
if(!b) return a;
if(l==r){
ans[a]=l;
sum[a]+=sum[b];
return a;
}
int mid=l+r>>1;
ls[a]=merge(ls[a],ls[b],l,mid);
rs[a]=merge(rs[a],rs[b],mid+1,r);
up(a);
return a;
}
void dfs(int u,int fa){
for(auto &v:adj[u]){
if(v==fa) continue;
dfs(v,u);
merge(rt[u],rt[v],1,MAXN-5);
}
if(sum[rt[u]])
res[rt[u]]=ans[rt[u]];
}
void solve() {
cin>>n>>m;
for(int i=1; i<n; i++) {
int u,v;
cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
++tot;
rt[i]=i;
}
++tot;
rt[n]=n;
tree_build(1,0);
tree_decom(1,1);
for(int i=1; i<=m; i++) {
int u,v,w;
cin>>u>>v>>w;
update(u,1,MAXN-5,w,1);
update(v,1,MAXN-5,w,1);
int tmp=getLCA(u,v);
update(tmp,1,MAXN-5,w,-1);
if(f[tmp])
update(f[tmp],1,MAXN-5,w,-1);
}
dfs(1,0);
for(int i=1;i<=n;i++){
cout<<res[i]<<"\n";
}
}
signed main() {
close;
solve();
}
题3 Dominant Indices
就是算每个点子树的距离的众数
对每个点的deep放进动态开点权值线段树中 然后找众数
对于每个节点 减去本身深度就是答案(好聪明
vector<int> adj[MAXN];
int ls[MAXN*23],rs[MAXN*23],sum[MAXN*23],ans[MAXN*23];
int res[MAXN],cnt,deep[MAXN];
void dfs_deep(int u,int fa){
for(auto &v:adj[u]){
if(v==fa) continue;
deep[v]=deep[u]+1;
dfs_deep(v,u);
}
}
void up(int now){
if(sum[ls[now]]<sum[rs[now]]){
ans[now]=ans[rs[now]];
sum[now]=sum[rs[now]];
}
else{
ans[now]=ans[ls[now]];
sum[now]=sum[ls[now]];
}
}
void update(int &now,int l,int r,int pos,int val){
if(!now) now=++cnt;
if(l==r) {
ans[now]=l;
sum[now]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(ls[now],l,mid,pos,val);
else update(rs[now],mid+1,r,pos,val);
up(now);
}
int merge(int a,int b,int l,int r){
if(!a) return b;
if(!b) return a;
if(l==r){
ans[a]=l;
sum[a]+=sum[b];
return a;
}
int mid=l+r>>1;
ls[a]=merge(ls[a],ls[b],l,mid);
rs[a]=merge(rs[a],rs[b],mid+1,r);
up(a);
return a;
}
void dfs(int u,int fa){
for(auto &v:adj[u]){
if(v==fa) continue;
dfs(v,u);
u=merge(u,v,1,MAXN-5);
}
update(u,1,MAXN-5,deep[u],1);
res[u]=ans[u]-deep[u];
}
void solve(){
int n;cin>>n;
for(int i=1;i<=n;i++) cnt++;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
deep[1]=1;
dfs_deep(1,0);
dfs(1,0);
for(int i=1;i<=n;i++){
cout<<res[i]<<"\n";
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通