3.22
线段树
如果相邻两个颜色均相同则删除当前颜色
thinking
经典题目有毒,读懂题目后,知道只需要对每一个相邻的三元素判定是否相同,进行按需要进行删除即可。
solution
class Solution {
public:
bool winnerOfGame(string& c) {
int n=c.size();
int a=0;int b=0;
for(int i=0;i<=n-3;++i) {
if(c[i]==c[i+1]&&c[i+1]==c[i+2]) {
if(c[i]=='A') ++a;
else ++b;
}
}
return a>b;
}
};
带标记的线段树
thinking
接着昨天的线段树来说,此题考虑到区间修改,我们不能在进行循环进行修改,因此我们需要改进一下线段树,我们引入一般的标记方法。
我们引入用来标记的树(数组V)对区间的更改进行适当地记录,具体方法如下:(我们不去证明该种方法地合理性)
- 插入
对于修改的区间\([l,r]\),我们将f,v对照着进行观看,从两者的根节点出发,寻找完美契合的区间,对于v,完美契合的区间,我们加上更新的差值,路径上的节点不去管理,对于f,从根节点到契合的区间端点,契合的端点处,我们不去管。路径上的节点值增加对应的区间的变动值。
- 查询
由于我们的插入方法,对于给定查询区间\([l,r]\),进而在两棵树上寻找对应的变动值,对于任意的查询区间来说,如果出现了完美契合,那么值就是根节点到当前区间的v值*区间长度+f[k],我们思考为什么是根节点到当前契合区间的路径的v的总和。从v的更新来看,每一次的区间更新,我们将区间落下,分成几段完全契合的片段,更新v值,当增大区间的时候,这部分的更新至就有可能落在上面,同时对该区间起到了修改作用,因此,需要遍历求和整条路经。
反过来,我们观察f,f始终更新的是大段的值(非契合区间),这样,v与f的结合使用,就解决了区间修改的问题。
solution
const int N=1e5+1;
int n,m;long long a[N],f[4*N],v[4*N];
inline void buildtree(int k,int l,int r) {
v[k]=0;
if(l==r) {
f[k]=a[l];
return;
}
int m=(l+r)>>1;
buildtree(k+k,l,m);
buildtree(k+k+1,m+1,r);
f[k]=f[k+k]+f[k+k+1];
}
inline void insert(int k,int l,int r,int x,int y,long long z) {
if(l==x&&r==y) {
v[k]+=z;
return;
}
f[k]+=(y-x+1)*z;
int m=(r+l)>>1;
if(y<=m)
insert(k+k,l,m,x,y,z);
else
if(x>m)
insert(k+k+1,m+1,r,x,y,z);
else
insert(k+k,l,m,x,m,z),
insert(k+k+1,m+1,r,m+1,y,z);
}
long long calc(int k,int l,int r,int x,int y,long long p) {
p+=v[k];
if(l==x&&y==r) {
return p*(r-l+1)+f[k];
}
int m=(l+r)>>1;
if(y<=m)
return calc(k+k,l,m,x,y,p);
else
if(x>m)
return calc(k+k+1,m+1,r,x,y,p);
else
return calc(k+k,l,m,x,m,p)+calc(k+k+1,m+1,r,m+1,y,p);
}
void solve() {
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
buildtree(1,1,n);
for(int i=0;i<m;++i) {
int index;cin>>index;
if(index==1) {
int x,y;cin>>x>>y;long long k;cin>>k;
insert(1,1,n,x,y,k);
} else {
int x,y;cin>>x>>y;
cout<<calc(1,1,n,x,y,0)<<'\n';
}
}
}

浙公网安备 33010602011771号