第一次学习线段树,树状数组RMQ等,努力学习,加油。
HDU 1166
题意:很清楚吧
题解:用树状数组求连续和,非常快。
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 50000+5 #define MAXN 100000+5 #define lson 2*i #define rson 2*i+1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int c[MAX]; int a[MAX]; int sum(int x){ int ans=0; while(x>0){ ans+=c[x]; x-=lowbit(x); } return ans; } void add(int x,int d){ while(x<=MAX){ c[x]+=d; x+=lowbit(x); } } int main(){ int T; scanf("%d",&T); for(int t=1;t<=T;t++){ int n; scanf("%d",&n); mem0(c); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); add(i,a[i]); } string s; printf("Case %d:\n",t); while(cin>>s){ int a,b; if(s[0]=='Q'){ scanf("%d%d",&a,&b); printf("%d\n",sum(b)-sum(a-1)); } else if(s[0]=='A'){ scanf("%d%d",&a,&b); add(a,b); } else if(s[0]=='S'){ scanf("%d%d",&a,&b); add(a,-b); } else if(s[0]=='E') break; } } return 0; }
HDU 1754
题意:都明白
题解:很明显是线段树,然而我第一次撸线段树,TLE RE各种死,首先这题有点阴,给20W点,肯定会RE,看discuss里说要开三倍大才能过
然后就是建树,更新,查询,我就是更新的地方写烦了然后T了好久,更新的点应该类似于二分找到那个位置。。(单点更新)
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 200000+5 #define MAXN 100000+5 #define lson 2*i #define rson 2*i+1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int maxv[MAX*12]; int a[MAX*3]; int l,r; int n,m; void build(int i,int L,int R){ int mid=L+(R-L)/2; if(L==R) maxv[i]=a[L]; else{ build(lson,L,mid); build(rson,mid+1,R); maxv[i]=max(maxv[lson],maxv[rson]); } } void update(int i,int L,int R,int l,int r){ int mid=L+(R-L)/2; if(L==R){ maxv[i]=r; return; } if(l<=mid) update(lson,L,mid,l,r); else update(rson,mid+1,R,l,r); maxv[i]=max(maxv[lson],maxv[rson]); } int query(int i,int L,int R){ int mid=L+(R-L)/2; int ans=0; int a=0,b=0; if(l<=L&&R<=r) return maxv[i]; if(l<=mid) a=query(lson,L,mid); if(r>mid) b=query(rson,mid+1,R); return ans=max(a,b); } int main(){ while(~scanf("%d%d",&n,&m)){ mem0(maxv); mem0(a); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); while(m--){ char c; getchar(); scanf("%c%d%d",&c,&l,&r); if(c=='Q') printf("%d\n",query(1,1,n)); else{ update(1,1,n,l,r); } } } return 0; }
poj 3468
题解:就是线段树区间修改值,看着是个水题,模板题,然而我T啊WA 啊RE啊大半天了,不行了搞得我困死了
学习了一发完全版线段树的模板,慢慢体会了,必须靠题量和时间慢慢理解了,谁叫上学期不认真没有早点学线段树呢(区间更新)
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 1000000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } LL sum[MAX<<2]; LL add[MAX<<2]; void PushUp(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void PushDown(int rt,int m){ if(add[rt]){ add[rt<<1]+=add[rt]; add[rt<<1|1]+=add[rt]; sum[rt<<1]+=add[rt]*(m-(m>>1)); sum[rt<<1|1]+=add[rt]*(m>>1); add[rt]=0; } } void build(int l,int r,int rt){ if(l==r){ scanf("%lld",&sum[rt]); return ; } int m=l+(r-l)/2; build(lson); build(rson); PushUp(rt); } void update(int L,int R,LL c,int l,int r,int rt){ if(L<=l&&r<=R){ add[rt]+=c; sum[rt]+=c*(r-l+1); } else{ PushDown(rt,r-l+1); int m=l+(r-l)/2; if(L<=m) update(L,R,c,lson); if(R>m) update(L,R,c,rson); PushUp(rt); } } LL query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return sum[rt]; PushDown(rt,r-l+1); int m=l+(r-l)/2; LL ans=0; if(L<=m) ans+=query(L,R,lson); if(R>m) ans+=query(L,R,rson); return ans; } int main(){ int n,q; mem0(add); scanf("%d%d",&n,&q); build(1,n,1); while(q--){ char ch; int a,b; LL c; getchar(); scanf("%c",&ch); if(ch=='Q'){ scanf("%d%d",&a,&b); printf("%lld\n",query(a,b,1,n,1)); } else{ scanf("%d%d%lld",&a,&b,&c); update(a,b,c,1,n,1); } } return 0; }
poj 2528
题意:给你n个海报,按照顺序贴墙上,问你最后能看到多少个(未被覆盖的有多少个)
题解:区间的维护,用线段树(虽然本渣一开始完全想不到线段树怎么弄啊)
这题数据有点大,所以需要离散化(排序去重
但是只是这样简单的离散化是错误的,
如三张海报为:1~10 1~4 6~10
离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。
新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)
X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10
这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3
最终,1~2为2,3为1,4~5为3,于是输出正确结果3。
(然而poj这么写了会WA,不这么离散反而能AC,不造为啥,反正知道这个方法是对的就行了)
线段树还得多刷题
<span style="font-size:12px;">#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 20000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int l[MAX],r[MAX]; int x[MAX<<3]; int root[MAX<<4]; int vis[MAX<<2]; int cnt; void PushDown(int rt){ root[rt<<1]=root[rt<<1|1]=root[rt]; root[rt]=-1; } void update(int l,int r,int rt,int c,int L,int R){ if(L<=l&&r<=R){ root[rt]=c; return; } if(root[rt]!=-1) PushDown(rt); int m=l+(r-l)/2; if(L<=m) update(lson,c,L,R); if(R>m) update(rson,c,L,R); } int Bsearch(int a,int b,int xx){ int l=a,r=b; while(l<=r){ int mid=(l+r)/2; if(x[mid]<xx) l=mid+1; else r=mid-1; } return l; } void query(int l,int r,int rt){ if(l==r){ if(!vis[root[rt]]){ cnt++; vis[root[rt]]=1; } return; } if(root[rt]!=-1) PushDown(rt); int m=l+(r-l)/2; query(lson); query(rson); } int main(){ int t; scanf("%d",&t); while(t--){ int n; scanf("%d",&n); int nn=0; for(int i=1;i<=n;i++){ scanf("%d%d",&l[i],&r[i]); x[++nn]=l[i]; x[++nn]=r[i]; } sort(x+1,x+nn+1); int mm=1; for(int i=2;i<=nn;i++){ if(x[i]!=x[i-1]) x[++mm]=x[i]; } mem1(root); mem0(vis); for(int i=1;i<=n;i++){ int L=Bsearch(1,mm,l[i]); int R=Bsearch(1,mm,r[i]); update(1,mm,1,i,L,R); } cnt=0; query(1,mm,1); printf("%d\n",cnt); } return 0; } </span>
hdu 1698
题意:屠夫的钩子有n段,每段的值为1,现在屠夫可以在一段区间内把钩子的值变成1,2,3;
题解:显然是线段树,区间替换,这题只需要更新,然后询问rt=1的点,所以没有写query函数,感觉还是比较清楚的(第一次写区间更换,感觉还得多刷点模板题,模板都没写熟练呢,哎)还有数组每次开4倍,*4总是RE,写成<<2就能过了,什么仇什么怨
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 100000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int sum[MAX<<2]; int col[MAX<<2]; void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void pushdown(int rt,int m){ if(col[rt]){ col[rt<<1]=col[rt<<1|1]=col[rt]; sum[rt<<1]=col[rt]*(m-(m>>1)); sum[rt<<1|1]=col[rt]*(m>>1); col[rt]=0; } } void build(int l,int r,int rt){ if(l==r){ sum[rt]=1; return; } int m=l+(r-l)/2; build(lson); build(rson); pushup(rt); } void update(int l,int r,int rt,int L,int R,int c){ if(L<=l&&r<=R){ col[rt]=c; sum[rt]=c*(r-l+1); return; } pushdown(rt,r-l+1); int m=l+(r-l)/2; if(L<=m) update(lson,L,R,c); if(R>m) update(rson,L,R,c); pushup(rt); } int main(){ int t; scanf("%d",&t); int kase=0; while(t--){ int n,q; scanf("%d%d",&n,&q); mem0(col); build(1,n,1); while(q--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); update(1,n,1,a,b,c); } printf("Case %d: The total value of the hook is %d.\n",++kase,sum[1]); } return 0; }
zoj 1610
题意:给你一段区间,从0-8000(这是个坑点,一开始以为是0-n,跪了会),然后给你n次涂色,每次可以覆盖以前的,问你最后墙上每种颜色出现了多少段(如果同种颜色中间被其他颜色或者没有颜色分开了,那么就是两段)
题解:线段树区间维护啊,然后给你的是点,让你涂色的两点之间的区间,所以把输入的左端点+1,把区间压缩成点,这样容易想一点,就是每次涂色的是一段点,然后求多少段。
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 8000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int col[MAX<<2]; int vis[MAX<<2]; int ans[MAX<<2]; int colour[MAX<<2]; void pushdown(int rt){ if(col[rt]!=-1){ col[rt<<1]=col[rt<<1|1]=col[rt]; col[rt]=-1; } } void update(int l,int r,int rt,int L,int R,int c){ if(L<=l&&r<=R){ col[rt]=c; return; } pushdown(rt); int m=l+(r-l)/2; if(L<=m) update(lson,L,R,c); if(R>m) update(rson,L,R,c); } void query(int l,int r,int rt){ if(l==r){ if(col[rt]!=-1){ ans[l]=col[rt]; } return; } pushdown(rt); int m=l+(r-l)/2; query(lson); query(rson); } int main(){ int n; while(~scanf("%d",&n)){ mem1(col); mem0(vis); mem1(ans); mem0(colour); for(int i=0;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); update(1,8000,1,a+1,b,c); } query(1,8000,1); for(int i=1;i<=8000;i++){ if(ans[i]!=-1){ if(i==1) colour[ans[i]]++; else if(ans[i]!=ans[i-1]) colour[ans[i]]++; } } for(int i=0;i<=8000;i++){ if(colour[i]) printf("%d %d\n",i,colour[i]); } printf("\n"); } return 0; }
题意:给你n个牛,求区间内最高的牛和最低的牛的差
题解:线段树维护区间内的最大最小值啊怒1A
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 50000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int a[MAX]; int maxv[MAX<<2]; int minv[MAX<<2]; int _max,_min; void pushup(int rt){ maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]); minv[rt]=min(minv[rt<<1],minv[rt<<1|1]); } void build(int l,int r,int rt){ if(l==r){ scanf("%d",&maxv[rt]); minv[rt]=maxv[rt]; return; } int m=l+(r-l)/2; build(lson); build(rson); pushup(rt); } void query(int l,int r,int rt,int L,int R){ if(L<=l&&r<=R){ _max=max(_max,maxv[rt]); _min=min(_min,minv[rt]); return ; } int m=l+(r-l)/2; if(L<=m) query(lson,L,R); if(R>m) query(rson,L,R); } int main(){ int n,q; scanf("%d%d",&n,&q); build(1,n,1); while(q--){ int a,b; scanf("%d%d",&a,&b); _max=0; _min=INF; query(1,n,1,a,b); printf("%d\n",_max-_min); } return 0; }
hdu 4027
题意:给你n个数,然后q次询问,t=0的时候把区间内的值都开根取整,t=1的时候就输出区间的和
题解:
刚开始的时候第一反应这道题是成段的更新,不能一个数一个数的更新,那样肯定会超时的!
但是再想了一会儿之后,发现成段更新比较困难,主要是这道题的每个节点的更新并不是统一的(也就是说并不是都加K,或者都减K之类的);
所以我们并不能像一般的成段更新那样去更新。
再仔细观察后,其实我们可以很容易的发现,一个数k(k<=2^63-1)在经过最多6,7次的开平方根后,必然会变成1,而且当1的平方根也是1;
也就是说当一个数为1的时候,我们没有必要对它进行操作和更新;而且一个很大的数仅仅经过6,7次就可以变成1;
所以到这里我们因该就可以形成一个解题的大体思路了:
每当我们要进行更新操作的时候,我们先判断一下这个区间是否有必要进行更新(若全都是1就没有更新的必要了);
判断的方法很简单:就是看该区间的长度和该区间内的总值是否相等;
然后要不停的更新
接下来的就是很水很水的区间求和了!!!!!!!!!!(给的区间X,Y有可能是X>Y,坑爹啊)
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 100000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } LL sum[MAX<<2]; LL ans; void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt){ if(l==r){ scanf("%I64d",&sum[rt]); return; } int m=l+(r-l)/2; build(lson); build(rson); pushup(rt); } void update(int l,int r,int rt,int L,int R){ if(sum[rt]==(LL)(r-l+1)) return; if(l==r){ sum[rt]=sqrt(double(sum[rt])); return; } int m=l+(r-l)/2; if(L<=m) update(lson,L,R); if(R>m) update(rson,L,R); pushup(rt); } void query(int l,int r,int rt,int L, int R){ if(L<=l&&r<=R){ ans+=sum[rt]; return; } int m=l+(r-l)/2; if(L<=m) query(lson,L,R); if(R>m) query(rson,L,R); } int main(){ int n; int kase=0; while(~scanf("%d",&n)){ kase++; printf("Case #%d:\n",kase); build(1,n,1); int q; scanf("%d",&q); while(q--){ int t,a,b; scanf("%d%d%d",&t,&a,&b); if(a>b) swap(a,b); if(t==0){ update(1,n,1,a,b); } else{ ans=0; query(1,n,1,a,b); printf("%I64d\n",ans); } } printf("\n"); } return 0; }
hdu 1394
题意:给你n个数字,然后这个序列可循环(就是可以每次把头上一个数字放到序列的最后),且这个序列的数字不重复,从0-n-1
然后问你这样的n个序列中逆序对数最小是多少
题解:我觉得这题好难啊,为啥过的人那么多而且AC率这么高。可能我太弱了吧,刚接触线段树
然后这题的思路是先找到输入的序列的逆序对数,然后头上一个数字放到最后,如果头上的数字是a[i],那么放到后面,逆序对数量的变化是-a[i]+n-1-a[i],所以只需要求出输入的数的逆序对数(貌似方法很多吧(分治+归并排序),线段树这个方法过于精妙)
线段树的方法是因为这些数字大小是0-n-1,然后输入一个数字的时候,逆序对数就是前面已经输入的比他大的数字的个数,所以可以每输入一个数字a,用它的大小对应的区间做出修改,就是等同于单点修改,在a点+1,然后更新区间,每次输入一个数字b的时候,就是求b-n-1这个区间里的和(这个区间里出现过的数组那个点就会+1)
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define MAX 10000+5 #define MAXN 100000+5 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lrt rt<<1 #define rrt rt<<1|1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) const int mod = 1000000007; const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; //读入外挂 inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; }while(isdigit(tmp=getchar())); return ret; } int a[MAX]; int sum[MAX<<2]; void pushup(int rt){ sum[rt]=sum[lrt]+sum[rrt]; } void update(int p,int l,int r,int rt){ if(l==r){ sum[rt]++; return; } int m=l+(r-l)/2; if(p<=m) update(p,lson); else update(p,rson); pushup(rt); } int query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return sum[rt]; int m=l+(r-l)/2; int ans=0; if(L<=m) ans+=query(L,R,lson); if(R>m) ans+=query(L,R,rson); return ans; } int main(){ int n; while(~scanf("%d",&n)){ int cnt=0; mem0(sum); for(int i=0;i<n;i++){ scanf("%d",&a[i]); cnt+=query(a[i],n-1,0,n-1,1); update(a[i],0,n-1,1); } int mini=INF; for(int i=0;i<n;i++){ cnt+=(n-1-2*a[i]); mini=min(mini,cnt); } printf("%d\n",mini); } return 0; }
hdu 2795
题意:给你一个h*w的木板,然后上面可以贴海报,海报是1*L的,然后每次都贴最上面的最左边的空位,问你每个海报贴在第几行
题解:我一开始都看不出是线段树啊,太弱了,h可以是10^9,然后吓住了,看了notonlysuccess写的blog,想想也是啊,每行都是一样宽,如果n行放不下,那么h行也放不下,所以h完全可以变成n,然后就是20W的数据量,就是维护每行的最大值
int maxv[MAX<<2]; int h,w,n; int cnt; void pushup(int rt){ maxv[rt]=max(maxv[lrt],maxv[rrt]); } void build(int l,int r,int rt){ if(l==r){ maxv[rt]=w; return; } int m=l+(r-l)/2; build(lson); build(rson); pushup(rt); } void query(int a,int l,int r,int rt){ if(maxv[rt]<a) return; if(cnt!=-1) return; if(l==r){ maxv[rt]-=a; cnt=l; return; } int m=l+(r-l)/2; query(a,lson); query(a,rson); pushup(rt); } int main(){ while(~scanf("%d%d%d",&h,&w,&n)){ if(h>n) h=n; build(1,n,1); while(n--){ int a; scanf("%d",&a); cnt=-1; query(a,1,h,1); printf("%d\n",cnt); } } return 0; }
poj 2828
题意:很多人插队,每个人有一个val,然后插在第几个人后面,最后输出这一列人的每个人的val
题解:这题难点在于想,我也是看了题解(哎虽然刷的是线段树专题,但是很多题完全想不到线段树啊,看来还是脑洞不行)
这题就是先输入,然后倒着更新,因为最后一个人的位置是确定的(pos+1),然后这样想,更新前面前面插队的人的时候,在他后面插队的对他没有影响,然后把后面插队的(已经插入线段树中的)去掉,在这些空位里,这个人也是在pos+1的位置上,然后开个sum数组记录这个区间内的空位,然后更新位置时候,如果这个位置大于左儿子的空位,那么就插入右儿子(需要减去左儿子的空位数)
int pos[MAX],val[MAX]; int sum[MAX<<2]; int col[MAX<<2]; int n; void pushup(int rt){ sum[rt]=sum[lrt]+sum[rrt]; } void build(int l,int r,int rt){ if(l==r){ sum[rt]=1; return ; } mid; build(lson); build(rson); pushup(rt); } void update(int p,int v,int l,int r,int rt){ if(l==r){ sum[rt]=0; col[rt]=v; return; } mid; if(p<=sum[lrt]) update(p,v,lson); else update(p-sum[lrt],v,rson); pushup(rt); } void query(int l,int r,int rt){ if(l==r){ printf("%d",col[rt]); if(l!=n) printf(" "); else printf("\n"); return; } mid; query(lson); query(rson); } int main(){ while(~scanf("%d",&n)){ for(int i=0;i<n;i++) scanf("%d%d",&pos[i],&val[i]); build(1,n,1); for(int i=n-1;i>=0;i--) update(pos[i]+1,val[i],1,n,1); query(1,n,1); } return 0; }
poj 3667
题意:给你n个房间,然后一开始都是空的,然后有长度为d的队伍要住进去,输出开始房间序号(要最小),不存在就输出0,还可以把一段房间内住的人都清空。
题解:显然是线段树了,如何判断一个区间内最长的连续的空房间是否大于a,需要参数sum(区间内最大连续个数),lsum(从最左端开始的最长连续个数),rsum(从右端开始的最长连续个数)
因为一个区间的最大连续个数sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt]));
然后还需要一个cover数组来记录当前区间是否被覆盖(住人)
这种需要前缀后缀的,求连续的线段树我还是不行啊,还得多练习,不能只会做万人轮的水题
int cover[MAX<<2],sum[MAX<<2],lsum[MAX<<2],rsum[MAX<<2]; void pushdown(int rt,int m){ if(cover[rt]!=-1){ cover[lrt]=cover[rrt]=cover[rt]; if(cover[rt]==0){ lsum[lrt]=rsum[lrt]=sum[lrt]=m-(m>>1); lsum[rrt]=rsum[rrt]=sum[rrt]=m>>1; } else{ lsum[lrt]=rsum[lrt]=sum[lrt]=lsum[rrt]=rsum[rrt]=sum[rrt]=0; } cover[rt]=-1; } } void pushup(int rt,int m){ lsum[rt]=lsum[lrt]; rsum[rt]=rsum[rrt]; if(lsum[rt]==m-(m>>1)) lsum[rt]+=lsum[rrt]; if(rsum[rt]==m>>1) rsum[rt]+=rsum[lrt]; sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt])); } void build(int l,int r,int rt){ cover[rt]=-1; sum[rt]=lsum[rt]=rsum[rt]=r-l+1; if(l==r) return; mid; build(lson); build(rson); } void update(int L,int R,int c,int l,int r,int rt){ if(L<=l&&r<=R){ cover[rt]=c; if(cover[rt]==0) sum[rt]=lsum[rt]=rsum[rt]=r-l+1; else sum[rt]=lsum[rt]=rsum[rt]=0; return; } pushdown(rt,r-l+1); mid; if(L<=m) update(L,R,c,lson); if(R>m) update(L,R,c,rson); pushup(rt,r-l+1); } int query(int a,int l,int r,int rt){ if(l==r) return 1; pushdown(rt,r-l+1); mid; if(sum[lrt]>=a) return query(a,lson); else if(rsum[lrt]+lsum[rrt]>=a) return m-rsum[lrt]+1; else query(a,rson); } int main(){ int n,m; scanf("%d%d",&n,&m); build(1,n,1); mem1(cover); while(m--){ int op; scanf("%d",&op); if(op==1){ int a; scanf("%d",&a); if(sum[1]<a) printf("0\n"); else{ int pos=query(a,1,n,1); printf("%d\n",pos); update(pos,pos+a-1,1,1,n,1); } } else{ int a,b; scanf("%d%d",&a,&b); update(a,a+b-1,0,1,n,1); } } return 0; }