Codeforces Round #254 (Div. 2)E题这题说的是给了一个一段连续的区间每个区间有一种颜色然后一个彩笔从L画到R每个区间的颜色都发生了 改变然后 在L和R这部分区间里所用的颜色变成了x 然后每个区间的 色度加上abs(x-Yi) Yi 为原位置的颜色,还有一个操作就是求 L 到 R 的距离之内的所有的点和,数据 有 n<=100000 m<100000 次操作 对于每次第二种操作输出, 自然我们将一个区间的颜色如果相同自然将他们 用延迟标记 但是 会有一个问题就是在一个带有延迟标记的区间操作两次或者两次以上会然后再去求这个延迟标记的某个区间的时候我们将无法进行,这里我们稍加修改就得到了我们想要的 就是 sum=(sum[o]-sum[lc]-sum[rc)/(R-L+1)这样就得到了我们对于每个子区间的再添加两次后的个数平均值然后进行一次向下的传递就ok了 这样每个区间得到了值sum[lc]+=sum*(lc->R - lc->L + 1); sum[rc] += sum*(rc->R - rc->L +1) 然后得到向下传递的值
#include#include #include <string.h> using namespace std; const int maxn =500005; __int64 _sum,x; int n,m,op,y1,y2; struct Itree{ __int64 sumv[maxn],color[maxn]; bool mark[maxn]; __int64 abs(__int64 a,__int64 b){ return (a-b)>0?(a-b):(b-a); } void build(int o, int L, int R){ if(L == R){ color[o]=L; sumv[o] =0; mark[o] =true; return ; } int M = L + ((R-L)>>1) ; build(o*2, L, M); build(o*2+1, M+1, R); mark[o] = false; sumv[o] = 0; } void pushdown(int o,int L,int R){ int lc=o*2,rc=o*2+1; __int64 sum= sumv[lc]+sumv[rc]; sum = (sumv[o]-sum)/(R-L+1); int M=L+((R-L)>>1); color[lc]=color[rc]=color[o]; sumv[lc]+=1LL*(M-L+1)*sum; sumv[rc]+=1LL*(R-M)*sum; mark[lc]=mark[rc]=true; } void update(int o,int L, int R){ int lc = o*2 , rc = o*2 + 1; if(y1<=L&&y2>=R&&mark[o]==true){ sumv[o] += 1LL*(R-L+1)*abs(color[o],x); color[o]=x; mark[o]=true; } else{ if(mark[o]==true) pushdown(o,L,R); mark[o] = false; int M =L+((R-L)>>1); if(y1<=M)update(lc,L,M); if(y2>M) update(rc,M+1,R); if(y1<=L&&y2>=R){ color[o]=x; mark[o]=true; } sumv[o]=sumv[lc]+sumv[rc]; } } void query(int o, int L, int R){ int lc =o*2,rc =o*2+1; if(y1<=L && y2>=R){ _sum+=sumv[o]; }else{ if(mark[o]==true) pushdown(o,L,R); mark[o]=false; int M = L + ( (R-L)>>1 ); if(y1 <= M) query(lc,L,M); if(y2 > M) query(rc,M+1,R); } } }tree; int main() { int n,m; scanf("%d%d",&n,&m); tree.build(1,1,n); for(int i =0; i i){ scanf("%d%d%d",&op,&y1,&y2); if(op==1){ scanf("%I64d",&x); tree.update(1,1,n); }else{ _sum=0; tree.query(1,1,n); printf("%I64d\n",_sum); } } return 0; }
uva12299 这 题 说 的 是 给 了 一 个 数 字 数 组 要 求 找 出 某 个 区 间 内 的 最 小 值 然 后 还 有 一 个 操 作 是 循 环 的 置换 选 中 的 几 个 数 字 然 后 使 用 线 段 树 的 单 点 更 新 查询用线段树 查找
#include#include #include <string.h> using namespace std; const int maxn =100005; int C[maxn]; int tem,y1,y2,v,_min; struct Itree{ int minv[maxn*4]; void build(int o,int L,int R){ if(L==R){ scanf("%d",&minv[o]); C[L]=minv[o]; return ; } int lc=o<<1,rc=(o<<1)|1; int M=L+( (R-L)>>1 ); build(lc,L,M); build(rc,M+1,R); minv[o]=min( minv[lc], minv[rc] ); } void query(int o,int L,int R){ int lc=o<<1,rc=(o<<1)|1; if(y1<=L&&y2>=R){ _min=min(_min,minv[o]); } else{ int M=L+((R-L)>>1); if(y1<=M) query(lc,L,M); if(y2 >M) query(rc,M+1,R); } } void update(int o,int L,int R){ int lc=o<<1,rc=(o<<1)|1; if(L==R){ minv[o]=v; } else{ int M=L+((R-L)>>1); if(tem<=M) update(lc,L,M); else update(rc,M+1,R); minv[o]=min(minv[lc],minv[rc]); } } }tree; char str[50]; int rt[100],len; int worth[100]; void jud(){ len=0; int L =strlen(str); int i=0; while(i<L){ if(str[i]>='0'&&str[i]<='9'){ int E = 0; while( str[i] >= '0' && str[i] <= '9'&&i<L){ E= E * 10 + str[i] - '0'; ++i; } rt[len++]=E; } ++i; } } int main() { int n,q; while( scanf("%d%d",&n,&q) == 2){ tree.build(1,1,n); getchar(); for(int i =0; i i){ gets(str); len=0; jud(); if(str[0]=='q'){ y1=rt[0]; y2=rt[1]; _min=2147483647; tree.query(1,1,n); printf("%d\n",_min); }else{ for(int x =0; xx) worth[x]=C[rt[(x+1)%len]]; for(int x =0; x< len ;++x){ tem=rt[x]; v=worth[x]; tree.update(1,1,n); } for(int x =0; x x){ C[rt[x]]=worth[x]; } } } } return 0; }
uva1232 这题说的是给 我们要在地平线上依次建造n座建筑物.建筑物的修建按照从后往前的顺序,因此新建物可能会挡住一部分的老建筑修建完一座建筑物之后,统计他在多长的部分是最高的可以等高并把这个长度称为该建筑物的覆盖度,接下来对线段树进行稍加修改就ok了 然后记得 不要用区间去表示 将他给的点缩成 L,R-1 建树的时候同样用这样的操作
#include#include #include <string.h> using namespace std; const int maxn = 400005; int L[100005],R[100005],C[100005]; int y1,y2,v,_sum; struct Itree{ int maxv[maxn],setv[maxn]; void pushdown(int o){ int lc = o<<1 , rc = (o<<1)|1; if(setv[o]>=0){ setv[lc]=setv[rc]=setv[o]; setv[o]=-1; } } void maintain(int o, int L, int R){ int lc = o<<1, rc=(o<<1)|1; if(R>L){ maxv[o]=max(maxv[lc], maxv[rc]); } if(setv[o]>=0){ maxv[o]=setv[o]; } } void query(int o,int L,int R){ int lc =o<<1, rc =( o<<1 )|1; maintain(o, L, R); if(setv[o]>v) return; if(setv[o]==v){ _sum+=min(R,y2) - max(L,y1) +1 ; return ; } if( y1 <= L && y2 >= R && maxv[o]<=v ){ _sum+=R-L+1; setv[o]=v; } else { int M =L+((R-L)>>1); pushdown( o ); if(y1<=M) query(lc,L,M); else maintain(lc,L,M); if(y2 >M) query(rc,M+1,R); else maintain(rc,M+1,R); } maintain(o,L,R); } }tree; int main() { int n,TR,ans,t; scanf("%d",&t); while(t --){ ans=0; scanf("%d",&n); TR=0; for(int i =0; i i){ scanf("%d%d%d",&L[i],&R[i],&C[i]); TR=max(R[i],TR); } tree.setv[1]=0; TR--; for(int i =0;i i){ _sum=0; v=C[i]; y1=L[i]; y2=R[i]-1; tree.query(1,1,TR); ans+=_sum; } printf("%d\n",ans); } return 0; }
uva11525 这个题说的是给了1到K的数列 求出这个数列 的 第n 大 当然按照字典序排列, 因为 n=Si(k-i)!+Si-1(k-(i-1))!+...S0(k-k)! 经过分析我们可以得出这样的一个结论就是Si 代表的是剩下的第几大的数 用前段树去查询 每 次 找 到 相 应 的 k值
#include#include #include <string.h> using namespace std; const int maxn = 50005*4; int tem,_s,_k,S[50005],ans[50005]; struct Itree{ int sumv[maxn]; void build(int o,int L,int R){ if(L==R){ sumv[o]=1; return ; } int M=L+((R-L)>>1); int lc=o<<1,rc=(o<<1)|1; build(lc, L, M); build(rc, M+1, R); maintain(o,L,R); } void maintain(int o,int L,int R){ int lc = o<<1, rc=(o<<1)|1; if(R>L){ sumv[o] =sumv[lc]+sumv[rc]; } } void query(int o,int L, int R){ int lc = o<<1, rc =(o<<1)|1; if(L==R){ _s=L; sumv[o]=0; return; } int M = L+((R-L)>>1); if(sumv[lc]>=_k){ query(lc,L,M); }else{ _k-=sumv[lc]; query(rc,M+1,R); } maintain(o,L,R); } }tree; int main() { int t,k; scanf("%d",&t); while(t--){ scanf("%d",&k); tree.build(1,1,k); for(int i =0; i i) scanf("%d",&S[i]); for(int i =0; i i){ _k=S[i]+1; tree.query(1,1,k); ans[i]=_s; } for(int i =0;i 1; ++i) printf("%d ",ans[i]); printf("%d\n",ans[k-1]); } return 0; }
uva11402 这题说的是 给了一排的 o1 串 有 3 种操作 1 将F L R L和R 之间的 变成1 E L R 将 L 和R 之间的变成 0 I 将 L 和 R 取 反, S L R 求 L 和 R 之间的 和 , 这样这个数 太大了有 1000000 个0 1 显然不能做 可是 他操作的区间只有 1000 个 通过离散操作区间求值 从而节省时间将 每 个 不 在 操 作 点 上 的 区 间 化 为 一 个 点 然 后 处理好值就ok了 离散了一下
#include#include #include <string.h> #include <set> #include
uva1455 这题说的是在一个 平面上有n个城市,初始时城市之间没有任何的双向道路相连,你的任务是一次执行以下指令.
road A B: 在城市A 和城市B 之间连接一条双向道路,保证这条道路不和其他道路在非端点处相交.
line C 询问一条 y = c 的水平线和多少个州相交.本指令中,C的小数部分保证为0.5
通过将y坐标离散化,然后得到了许多的区间 然后 通过并查集不断地去维护这个区间, 每 两 个 区间 合 并 的 时 候 就该 去 判 断 区 间 与 区 间 之 间 的 联 系 然 后 得 到 最 终 的 更 新 目 的 这 题 变 成,了成段更新单点查询 这代码中有解释
#include#include #include <string.h> #include <set> #include using namespace std; const int maxn= 100005; struct point{ int x,y; }Pcity[maxn]; int y1,y2,pv,zv,tem,_state,_city; struct Itree { int city[maxn*4],state[maxn*4]; void build(int o,int L, int R) { city[o]=state[o]=0; if(L==R) return ; int M = L+((R-L)>>1); build(o<<1,L,M); build((o<<1)|1,M+1,R); } void pushdown(int o) { int lc =o<<1, rc = (o<<1)|1 ; city[lc] += city[o]; city[rc] += city[o]; state[lc]+= state[o]; state[rc]+= state[o]; state[o] = city[o] = 0 ; } void update(int o, int L, int R) { int lc=o<<1, rc =(o<<1)|1; if(y1<=L &&y2>=R) { state[o] += zv; city[o] += pv; } else{ int M = L+((R-L)>>1); pushdown(o); if( y1 <= M ) update(lc,L,M); if( y2 > M ) update(rc,M+1,R); } } void query(int o, int L, int R) { int lc=o<<1, rc=(o<<1)|1; if(R==L){ _state = state[o]; _city = city[o]; return; } else { int M = L + ( (R-L)>>1 ); pushdown( o ); if(tem<=M)query( lc, L, M ); else query( rc, M+1, R ); } } }tree; int C[100005],DOWNm[100005],UPm[100005],per[100005],len,num[100005]; char str[20]; int RE[2000002]; int finde(int x) { return per[x]==x?x:(per[x]=finde(per[x])); } void upoperator(int Lloc, int Rloc, int pvw, int zvw) { y1 = RE[Lloc]+1; y2 =RE[Rloc]; pv = pvw; zv = zvw; tree.update(1,1,len); } void intf(int p1, int p2) { int DOWN =min(DOWNm[p1],DOWNm[p2]) , UP= max(UPm[p1],UPm[p2]); per[p1]=p2; num[p2]+=num[p1]; DOWNm[p2]=DOWN; UPm[p2]=UP; } void OneToSet(int p1, int p2) { if( DOWNm[p1] >= DOWNm[p2] && DOWNm[p1] <= UPm[p2] ) {//p1点在 p2集合 区间内 upoperator( DOWNm[p2],UPm[p2],num[p1],0 ); intf(p1,p2); return ; } if( DOWNm[p1] > UPm[p2] ) {// p1这个点在 p2集合的上面 upoperator(UPm[p2],DOWNm[p1],num[p2]+num[p1],1); upoperator(DOWNm[p2],UPm[p2],num[p1],0); intf(p1,p2); return; } if( DOWNm[p1] < UPm[p2] ) {// p1 这个点在 p2集合的 下面 upoperator( DOWNm[p1], DOWNm[p2], num[p1]+num[p2], 1); upoperator( DOWNm[p2], UPm[p2],num[p1],0); intf(p1,p2); return ; } } void intersect(int p1, int p2) { if(UPm[p1]-UPm[p2]>0) upoperator(UPm[p2], UPm[p1], num[p2],0); if(UPm[p2]-DOWNm[p1]>0) upoperator(DOWNm[p1],UPm[p2],0,-1); if(DOWNm[p1]-DOWNm[p2]>0) upoperator(DOWNm[p2] , DOWNm[p1], num[p1],0); intf(p1,p2); } void SetToSet(int p1, int p2) { if( DOWNm[p1] > UPm[p2] ) {//相离 p1 在上面 upoperator( DOWNm[p1], UPm[p1], num[p2], 0); upoperator( UPm[p2], DOWNm[p1], num[p2]+num[p1], 1); upoperator( DOWNm[p2], UPm[p2], num[p1], 0); intf(p1,p2); return ; } if( DOWNm[p2] > UPm[p1] ) {//相离 p2 在上面 upoperator(DOWNm[p2], UPm[p2], num[p1], 0); upoperator(UPm[p1], DOWNm[p2], num[p1]+num[p2],1); upoperator(DOWNm[p1], UPm[p1], num[p2], 0); intf(p1,p2); return ; } if( DOWNm[p1] >= DOWNm[p2] && UPm[p1] <= UPm[p2] ) {//包含 if(UPm[p2] -UPm[p1]>0) upoperator(UPm[p1], UPm[p2] , num[p1],0); if(UPm[p1]-DOWNm[p1]>0) upoperator(DOWNm[p1],UPm[p1],0,-1); if(DOWNm[p1]-DOWNm[p2]>0) upoperator(DOWNm[p2], DOWNm[p1], num[p1],0); intf(p1,p2); return ; } if( UPm[p1]>UPm[p2]) // 相交 intersect(p1,p2); // p1 在p2 的上面 else intersect(p2,p1); // p1 在p2 的下面 } void solve1(int p1, int p2) { int fp1 =finde(p1); int fp2 =finde(p2); if(fp1 == fp2) return; if( DOWNm[fp1] == UPm[fp1]&& DOWNm[fp2] == UPm[fp2] && DOWNm[fp1] == DOWNm[fp2] ) { // fp1 和 fp2 都是各自在同一行的点 或者集合 但是 他们 在同一行 intf(fp1,fp2); return ; } if(DOWNm[fp1]==UPm[fp1] && DOWNm[fp2]==UPm[fp2] && DOWNm[fp1] != DOWNm[fp2]) {// fp1 和 fp2 都是各自在同一行的点 或者集合 但是 他们 不在同一行 int DOWN= min(DOWNm[fp1],DOWNm[fp2]), UP =max(UPm[fp1],UPm[fp2]); upoperator( DOWN, UP, num[fp2]+num[fp1], 1 ); intf(fp1,fp2);return ; } if( DOWNm[fp1] == UPm[fp1] && DOWNm[fp2] != UPm[fp2] ) {// fp1 是一个点 fp2 是一个集合 OneToSet(fp1,fp2); return ; } if(DOWNm[fp1] != UPm[fp1] && DOWNm[fp2] == UPm[fp2]) {// fp2 是一个点 fp1 是一个集合 OneToSet(fp2,fp1); return ; } int R1 = UPm[fp1] - DOWNm[fp1],R2=UPm[fp2] - DOWNm[fp2]; // 剩下的只可能是两个集合 计算两个集合的大小 if(R1<=R2) SetToSet(fp1,fp2); // fp1 的集合小于 fp2 的集合 else SetToSet(fp2,fp1); // fp2 的集合小于 fp1 的集合 } void solve2(int h) { int t = lower_bound(C,C+len+1,h) - C; tem=t; tree.query(1,1,len); printf("%d %d\n",_state,_city); } int main() { int T,n; scanf("%d",&T); for(int cas =1; cas <=T ; ++cas) { set<int> UN; scanf("%d",&n); for(int i =0; i i) { scanf("%d%d",&Pcity[i].x,&Pcity[i].y); Pcity[i].y*=2; per[i]=i; UPm[i] = DOWNm[i] = Pcity[i].y; num[i]=1; UN.insert(Pcity[i].y); } UN.insert(0);UN.insert(1000000*2); len=0; for(set<int>::iterator it = UN.begin(); it!=UN.end() ; ++it) { int to = *it; RE[to] =len; C[ len ++ ] = to; } len--; tree.build(1,1,len); memset(tree.city,0,sizeof(tree.city)); memset(tree.state,0,sizeof(tree.state)); int m; scanf("%d",&m); for(int i =0; i i) { scanf("%s",str); if(str[0]=='r') { int p1,p2; scanf("%d%d",&p1,&p2); solve1(p1,p2); } else { double red; scanf("%lf",&red); solve2(red*2); } } UN.clear(); } return 0; }
446C DZY Loves Fibonacci Numbers 这题说的是给了一组数从1编号到n 每个数都有初值, 有两种操作分别是1 l r 将 l和r 区间内的数 相对应的每个位置加上斐波那契f[1] 到f[r-l+1],通过数学方法他们证出斐波那契的通项
然后发现同余 然后这个 然后接下来的我就知道了 通过求逆得到
这个 可以用线段树来解决 因为我们知道等比数列相加和仍然是等比 然后通过这个每个线段树都有一个标记 标记的是 该数列的第一项然后 向下更新的时候就是 让 per[lc]+=per[o] per[rc]=per[lc]*q^(M+1-L) 然后得到了正解 虽然和其他的原理相同 还是把他敲了一遍 如果是自己想出来的 那就更有成就感了
#include#include <string.h> #include using namespace std; const __int64 mod = 1000000009; const int maxn=300005; __int64 q1=691504013,q2=308495997,req1=308495996,req2=691504012,Y=276601605; __int64 F[maxn],A[maxn]; __int64 pow_2(int n,__int64 q) { __int64 a=q; __int64 ans=1; while(n>0) { if(n&1) ans=(ans*a)%mod; a=(a*a)%mod; n/=2; } return ans; } __int64 _sum; int y1,y2; struct Itree { __int64 per[maxn*4],fer[maxn*4],sumv[maxn*4],falg[maxn*4],addv[maxn*4]; void pushdown(int o,int L,int R) { int lc=o*2,rc=o*2+1; int M = (L+R)/2; if(falg[o]) { if(per[o]!=0){ per[lc]=( per[lc]+per[o])%mod; per[rc]=( per[rc]+per[o]*F[M+1-L])%mod; per[o]=0; } if(fer[o]!=0){ fer[lc] = (fer[lc]+fer[o])%mod; fer[rc] = (fer[rc]+fer[o]*A[M+1-L])%mod; fer[o]=0; } falg[o]=false; falg[lc]=true; falg[rc]=true; } } void maintain(int o,int L, int R) { int lc=o*2,rc =o*2+1; sumv[o]=0; if(falg[o]) { __int64 qn1=F[R-L+1]; __int64 qn2=A[R-L+1]; __int64 t1 =( ( ( ( ( 1-qn1+ mod )%mod ) *req1 )%mod) *per[o] )%mod; __int64 t2 =( ( ( ( ( 1-qn2+ mod )%mod ) *req2 )%mod) *fer[o] )%mod; sumv[o]=( ( ( (t1-t2)%mod + mod )%mod ) * Y )%mod; } sumv[ o ] += addv[ o ] ; if( L<R ){ sumv[o] += sumv[lc] + sumv[rc] ; } } void update(int o,int L,int R) { int lc=o*2,rc=o*2+1; if(y1<=L&&y2>=R){ per[o] = (per[o]+(q1*F[L-y1])%mod)%mod; fer[o] = (fer[o]+(q2*A[L-y1])%mod)%mod; falg[o]=true; }else{ int M =(L+R)/2; pushdown(o,L,R); if(y1<=M)update(lc,L,M); else maintain(lc,L,M); if(y2>M)update(rc,M+1,R); else maintain(rc,M+1,R); } maintain(o,L,R); } void query(int o,int L,int R) { int lc =o*2, rc =o*2+1; if(y1<=L&&y2>=R){ _sum=(_sum+sumv[o])%mod; }else{ int M=(L+R)/2; pushdown(o,L,R); maintain(lc,L,M); maintain(rc,M+1,R); if(y1<=M)query(lc,L,M); if(y2>M)query(rc,M+1,R); } } void build(int o, int L, int R) { addv[o]=per[o]=fer[o]=0; falg[o]=false; if(L==R){ scanf("%I64d",&sumv[o]); addv[o]=sumv[o]; return; } int lc =o*2,rc =o*2+1; int M = (L+R)/2; build(lc,L,M); build(rc,M+1,R); sumv[o]=sumv[lc]+sumv[rc]; } }tree; void gcd(__int64 a,__int64 b,__int64 &g,__int64 &x,__int64 &y) { if(b==0){ g=a ; x=1; y=0; }else{ gcd(b,a%b,g,y,x); y-=x*(a/b); } } __int64 exd(__int64 a,__int64 b) { __int64 x,y,g; gcd(a,b,g,x,y); return g==1?(x+b)%b:-1; } __int64 C[100],R[100]; int main() { int n,m; A[0]=F[0]=1; for(int i =1 ; i<=300000; ++i) { F[i]=(F[i-1]*q1)%mod; A[i]=(A[i-1]*q2)%mod; } scanf("%d%d",&n,&m); tree.build(1,1,n); for(int i =0; i i) { int c; scanf("%d%d%d",&c,&y1,&y2); if(c==1) tree.update(1,1,n); else { _sum=0; tree.query(1,1,n); printf("%I64d\n",(_sum)%mod); } } return 0; }