「NOIP2018模拟9.17」线段树 - 思路题
线段树 (segmenttree.c/cpp/pas)
Input file: segmenttree.in
Output file: segmenttree.out
Time Limit : 3 seconds
Memory Limit: 512 megabytes
线段树(Segment Tree)是一种二叉树,可视为树状数组的变种,最早出现在 2001 年,由程式竞 赛选手发明。此数据结构实际应用用途不大,但由于其程式易于实作而被广泛应用于程式竞赛当中。 其用途是在 O(logN) 查询一个指定区间内的信息,并可在同样的时间复杂度支持更新等操作。
为了学习线段树,你得到了一个长度为 n 的数组 a(下标从 1 开始),所有元素初始值均为 0 。 你需要进行 q 次操作,操作有三种:
1 x y 表示:把 a[x] 的值修改为值修改为 y 。
2 x y 表示:把 a[x] 的值修改为值修改为 a[x] + y 。
3 y 表示:把数组中全部的值修改为 y 。
进行每次操作之后,你都要查询数组中所有元素的和。
本来这道题是在仙人掌上对一条路径操作并支持历史版本查询,鉴于这只是一套 NOIP 模拟题的 签到题,你只需要解决这个弱化版的问题。
Input
第 1 行为两个整数 n,q ,以空格隔开。
接下来 q 行,每行格式为 1 x y 或 2 x y 或 3 y 。
Output
输出 q 行,每行表示进完当前操作后数组中所有元素的和。
Examples
segmenttree.in
5 3
1 3 3
3 2
2 3 3
segmenttree.out
3
10
13
大样例见下发文件。
Notes
对于 10% 的数据,n = 1,q ≤ 10 。
对于 40% 的数据,n ≤ 104,q ≤ 103 。
对于 70% 的数据,n ≤ 106,q ≤ 105 。
对于 100% 的数据,1 ≤ n ≤ 107,1 ≤ q ≤ 106,1 ≤ x ≤ n,0 ≤ y ≤ 100 。
本题输入规模较大,为减小输入数据需要的时间对你的程序效率造成的影响,在下发文件里提供 了一个基于 fread 的输入输出模板。如果你要使用该模板,请在文件输入输出而不是标准输入输出 下测试你的程序。
思路
法1:线段树+懒标记
看时空限制,发现其实是可以线段树做的,但是区间修改需要用到懒标记
提交之后最慢的点跑了2.2s
法2:玄学O(n)做法
不用线段树,维护a数组和sum值
再维护一个数组c,表示各数的层数,用cnt代表当前层
在操作3时将层数增加并用flag记录修改后的数值
如果在操作1或2时单点修改的层数与cnt不匹配
将该点的层数赋成cnt,绕后用flag标记的值来修改
完成O(1)查询
代码
法1:
#include<cstdio>
#include<cctype>
#include<ctime>
#include<iostream>
#define rg register
using namespace std;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){
rg int f=0,x=0;
rg char ch=gc();
while(!isdigit(ch)) f|=(ch=='-'),ch=gc();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
return f?-x:x;
}
const int N =1e7+1;
int n,m;
long long ans;
struct tree{
int l,r,sum,lazy;
}tr[N<<2];
inline void pushup(rg int o){
tr[o].sum=tr[o<<1].sum+tr[o<<1|1].sum;
}
inline void pushdown(rg int o){
tr[o<<1].lazy=(tr[o].lazy+tr[o<<1].lazy);
tr[o<<1|1].lazy=(tr[o].lazy+tr[o<<1|1].lazy);
tr[o<<1].sum=(tr[o<<1].r-tr[o<<1].l+1)*tr[o].lazy;
tr[o<<1|1].sum=(tr[o<<1|1].r-tr[o<<1|1].l+1)*tr[o].lazy;
tr[o].lazy=0;
}
inline void btree(rg int l,rg int r,rg int o){
tr[o].l=l,tr[o].r=r;
if(l==r) return ;
rg int mid=l+r>>1;
btree(l,mid,o<<1);
btree(mid+1,r,o<<1|1);
}
inline void modify(rg int i,rg int val,rg int o){
if(tr[o].l==i&&tr[o].r==i){
tr[o].sum=val;
return ;
}
rg int mid=tr[o].l+tr[o].r>>1;
if(i>mid) modify(i,val,o<<1|1);
else modify(i,val,o<<1);
pushup(o);
}
inline void add(rg int i,rg int val,rg int o){
if(tr[o].l==tr[o].r&&tr[o].l==i){
tr[o].sum+=val;
return ;
}
rg int mid=tr[o].l+tr[o].r>>1;
if(i>mid) add(i,val,o<<1|1);
else add(i,val,o<<1);
pushup(o);
}
inline void update(rg int l,rg int r,rg int val,rg int o){
if(tr[o].l==tr[o].r){
tr[o].sum=val;
return ;
}
if(tr[o].lazy) pushdown(o);
rg int mid=tr[o].l+tr[o].r>>1;
if(mid>=r) update(l,r,val,o<<1);
else if(l>mid) update(l,r,val,o<<1|1);
else{
update(l,mid,val,o<<1);
update(mid+1,r,val,o<<1|1);
}
pushup(o);
}
inline void check(rg int l,rg int r,rg int o){
if(tr[o].l==l&&tr[o].r==r){
ans+=tr[o].sum;
return ;
}
if(tr[o].lazy) pushdown(o);
rg int mid=tr[o].l+tr[o].r>>1;
if(mid>=r) check(l,r,o<<1);
else if(l>mid) check(l,r,o<<1|1);
else{
check(l,mid,o<<1);
check(mid+1,r,o<<1|1);
}
pushup(o);
}
signed main(){
freopen("segmenttree.in","r",stdin);
freopen("segmenttree.out","w",stdout);
n=read();m=read();
btree(1,n,1);
for(rg int i=1,jud,x,y;i<=m;++i){
jud=read();
if(jud==1){x=read();y=read();modify(x,y,1);}
else if(jud==2){x=read();y=read();add(x,y,1);}
else{y=read();update(1,n,y,1);}
ans=0;
check(1,n,1);
printf("%lld\n",ans);
}
return 0;
}
/*
5 3
1 3 3
3 2
2 3 3
ans=3
10
13
*/
法2:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXSIZE=50000020; //读入缓存大小,不要改动
int bufpos;
char buf[MAXSIZE];
int re(){ //读入一个int类型的整数
int val = 0;
for(; buf[bufpos] < '0' || buf[bufpos] > '9'; bufpos ++);
for(; buf[bufpos] >= '0' && buf[bufpos] <= '9'; bufpos ++)
val = val * 10 + buf[bufpos] - '0';
return val;
}
int n,q,a[10000010],sum,flag,c[10000010],cnt;
int main(){
freopen("segmenttree.in","r", stdin);
freopen("segmenttree.out","w", stdout);
buf[fread(buf, 1, MAXSIZE, stdin)] = '\0';
bufpos = 0;
n = re(); q = re();
for(int i = 1; i <= q; i ++){
int opt = re();
if(opt == 1){
int x = re(), y = re();
if(c[x]<cnt)a[x]=flag,c[x]=cnt;
sum+=y-a[x];
a[x]=y;
printf("%d\n",sum);
}
if(opt == 2){
int x = re(), y = re();
if(c[x]<cnt)a[x]=flag,c[x]=cnt;
sum+=y;
a[x]+=y;
printf("%d\n",sum);
}
if(opt == 3){
int y = re();
flag=y;
cnt++;
sum=y*n;
printf("%d\n",sum);
}
// addedge(u, v, w);
}
return 0;
}
后话
写这篇随笔的时候全程没有用\(LaTeX\)可能看着会有点不舒服
因为实在懒得用这个东西了我写题解主要是给自己看的希望如果别人看的话请见谅