2017暑假集训 div1 线段树(2)
来源:互联网 发布:五线谱变简谱软件 编辑:程序博客网 时间:2024/06/06 01:28
HDU 4027
题意:给一串数字,两种操作,操作1:区间所有数字开根号,操作二:求区间和
做法:线段树即可,但进行操作1 时先判断一下是否区间和为它的长度,如果是就可以不用更改了(没有这个会T)
#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>#include <cmath>#define lson rt<<1,begin,mid#define rson rt<<1|1,mid+1,endusing namespace std;typedef long long ll;ll tree[100005*4];void pushup(int rt){ tree[rt]=tree[rt<<1]+tree[rt<<1|1];}void build(int rt,int begin,int end){ if(begin==end) { scanf("%I64d",&tree[rt]); return; } int mid=(begin+end)>>1; build(lson) ; build(rson); pushup(rt);}void updata(int rt,int begin,int end,int l,int r){ if(begin==end) { tree[rt]=sqrt(tree[rt]); return; } int mid=(begin+end)>>1; if(mid>=l) updata(lson,l,r); if(r>mid) updata(rson,l,r); pushup(rt);}ll query(int rt,int begin,int end,int l,int r){ if(begin>=l&&r>=end) { return tree[rt]; } ll ans=0; int mid=(begin+end)>>1; if(mid>=l) ans+=query(lson,l,r); if(r>mid) ans+=query(rson,l,r); return ans;}int main(){ int n; int t=0; while(scanf("%d",&n)!=EOF) { build(1,1,n); int m; scanf("%d",&m); printf("Case #%d:\n",++t); while(m--) { int op,a,b; scanf("%d%d%d",&op,&a,&b); if(a>b) swap(a,b); if(op==0) { if(query(1,1,n,a,b)!=b-a+1) updata(1,1,n,a,b); } else { printf("%I64d\n",query(1,1,n,a,b)); } } printf("\n"); } return 0;}
HDU 1540
题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少
做法:正解是线段树的合并!!!
但其实stl set判断当前点左右两边被摧毁的距离也可以(注意加入0,和n+1)边界
#include <iostream>#include <stdio.h>#include <algorithm>#include <stack>#include <string.h>#include <set>using namespace std;int n,m;set<int> myset;stack<int> d;int main(){ while(scanf("%d%d",&n,&m)!=EOF) { while(!d.empty()) d.pop(); myset.clear(); myset.insert(0); myset.insert(n+1); set<int>::iterator l,r; char str[10]; while(m--) { scanf("%s",str); if(str[0]=='D') { int a; scanf("%d",&a); myset.insert(a); d.push(a); } else if(str[0]=='R') { if(d.empty()) continue; int a=d.top(); d.pop(); myset.erase(a); } else { int a; scanf("%d",&a); if(myset.count(a)!=0) printf("0\n"); else { l=myset.lower_bound(a); r=myset.upper_bound(a); l--; printf("%d\n",*r-*l-1); } } } } return 0;}
HDU 3974
题意:
- 一个公司里面每个员工都有一个顶头上司,一旦给某个员工分配任务后,这个员工以及该员工的所有下属都在做该任务。
- 有若干操作,分配给员工任务以及查询该员工正在执行的任务。
做法:首先线段树是很容易想到的,那关键是怎么在树上跑线段树,其实我们可以先用出入度找出树的根,然后DFS找出dfs序,一个点两次出现的中间点都是该点的子孙!!!
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>using namespace std;#define lson rt<<1,begin,mid#define rson rt<<1|1,mid+1,endconst int maxn=50100*2;int tree[maxn<<4];void pushdown(int rt){ if(tree[rt]!=-1) { tree[rt<<1]=tree[rt<<1|1]=tree[rt]; tree[rt]=-1; }}void updata(int rt,int begin,int end,int l,int r,int x){ if(begin>=l&&r>=end) { tree[rt]=x; return ; } pushdown(rt); int mid=(begin+end)>>1; if(mid>=l) updata(lson,l,r,x); if(r>mid) updata(rson,l,r,x);}int query(int rt,int begin,int end,int pos){ if(begin==end) { return tree[rt]; } pushdown(rt); int mid=(begin+end)>>1; if(mid>=pos) return query(lson,pos); else return query(rson,pos);}int n,m;int head[maxn],cnt=0;struct node{ int to,next;}edge[maxn];void add(int u,int v){ edge[cnt].next=head[u]; edge[cnt].to=v; head[u]=cnt++;}int in[maxn];int fis[maxn],num=0;int sec[maxn];void dfs(int pos){ fis[pos]=num++; for(int i=head[pos];i!=-1;i=edge[i].next) { int v=edge[i].to; if(fis[v]==-1) { dfs(v); } } sec[pos]=num++;}int main(){ int T,kiss=1; scanf("%d",&T); while(T--) { memset(head,-1,sizeof(head)); cnt=0; memset(fis,-1,sizeof(fis)); num=1; memset(tree,-1,sizeof(tree)); scanf("%d",&n); for(int i=1;i<=n;++i) in[i]=0; for(int i=1;i<n;++i) { int a,b; scanf("%d%d",&a,&b); add(b,a); in[a]++; } int root; for(int i=1;i<=n;++i) { if(in[i]==0) { root=i; break;} } dfs(root); printf("Case #%d:\n",kiss++); scanf("%d",&m); char str; while(m--) { scanf(" %c",&str); if(str=='C') { int a; scanf("%d",&a); printf("%d\n",query(1,1,2*n,fis[a])); } else if(str=='T') { int x,y; scanf("%d%d",&x,&y); updata(1,1,2*n,fis[x],sec[x],y); } } } return 0;}
HDU 4578
题意:一开始有n个为0的数,一共有四种操作:区间[l,r]内的数全部加c。区间[l,r]内的数全部乘c。区间[l,r]内的数全部初始为c。询问区间[l,r]内所有数的P次方之和
做法:线段树,每个树节点有 sum1,2,3,分别表示 1,2,3次方的和。lazy数组有 muit 和 add 表示没有乘的和没有加的。 每次 操作时候 把点看做 ax+b ,然后根据数学可以由 1,2次方更新三次方,由1次方可以更新出2次方,由加法可以更新出1 次方。注意 : 更新的时候先把 ax算出来 再把b 加进去 ,更新次方和的时候先 3, 再 2,最后 1
#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>#include <queue>#define lson rt<<1 ,begin ,mid#define rson rt<<1|1 , mid+1 ,endusing namespace std;const int mod=10007;struct node{ int add; int muit; int sum[3];}tree[100005*8];void cal(int rt,int muil,int add,int len){ tree[rt].sum[0] = tree[rt].sum[0] * muil % mod; tree[rt].sum[1] = tree[rt].sum[1] * muil % mod * muil % mod ; tree[rt].sum[2] = tree[rt].sum[2] * muil % mod * muil % mod * muil % mod ; tree[rt].muit = (tree[rt].muit * muil) % mod; tree[rt].add = ((tree[rt].add * muil ) % mod + add ) % mod; tree[rt].sum[2] = (tree[rt].sum[2] + len * add % mod * add % mod * add % mod) % mod; tree[rt].sum[2] = (tree[rt].sum[2] + 3* tree[rt].sum[0] % mod * add %mod *add %mod ) % mod; tree[rt].sum[2] = (tree[rt].sum[2] + 3 * tree[rt].sum[1] %mod *add % mod) % mod; tree[rt].sum[1] = (tree[rt].sum[1] + 2* tree[rt].sum[0] %mod * add % mod) % mod ; tree[rt].sum[1] = (tree[rt].sum[1] + len * add % mod *add % mod) % mod; tree[rt].sum[0] = (tree[rt].sum[0] + add * len % mod) %mod;}void pushup(int rt){ for(int i=0;i<3;++i) tree[rt].sum[i] = ( tree[rt<<1].sum[i] + tree[rt<<1|1].sum[i] ) %mod;}void pushdown(int rt,int k){ if(tree[rt].add ==0 && tree[rt] .muit ==1) return; if(k==1) return ; cal(rt<<1,tree[rt].muit,tree[rt].add,k-k/2); cal(rt<<1|1,tree[rt].muit,tree[rt].add,k/2); tree[rt].muit =1; tree[rt].add= 0;}void build(int rt,int begin,int end){ tree[rt].add=0; tree[rt].muit=1; for(int i=0;i<3;++i) tree[rt].sum[i]=0; if(begin==end) return ; int mid=(begin+end)>>1; build(lson) ; build(rson);}void updata(int rt, int begin ,int end,int l,int r,int muil ,int add){ if(begin>=l&&r>=end) { cal(rt,muil,add,end-begin+1); return ; } pushdown(rt,end-begin+1); int mid=(begin+end)>>1; if(mid>=l) updata(lson,l,r,muil,add); if(r>mid) updata(rson,l,r,muil,add); pushup(rt);}int query(int rt,int begin ,int end,int l,int r,int x){ if(begin>=l && r>=end) { return tree[rt].sum[x-1]; } pushdown(rt,end-begin+1); int ans=0; int mid=(begin+end)>>1; if(mid>=l) ans = (ans + query(lson,l,r,x) % mod) %mod; if(r>mid) ans = (ans +query(rson ,l ,r ,x) %mod ) %mod; return ans % mod;}int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0) break; build(1,1,n); while(m--) { int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); if(a==1) updata(1,1,n,b,c,1,d); else if(a==2) updata(1,1,n,b,c,d,0); else if(a==3) updata(1,1,n,b,c,0,d); else printf("%d\n",query(1,1,n,b,c,d) % mod); } } return 0;}
HDU 4614
现在要你插花,有n个花瓶,m次操作,初始花瓶中无花,操作有两种方式
操作1:1 a b,从编号为a的花瓶开始插花,共插b朵花,花只能插到无花的花瓶中,如果最后插不完b朵花,剩下的花舍弃掉
操作2:1 a b,把从编号a到编号b的所有花瓶里的花全部清理掉
对于操作1,需要输出开始插花的瓶子编号,和最后插花的瓶子编号
对于操作2,需要输出在a~b中总共清理了多少个花瓶中的花
操作1:1 a b,从编号为a的花瓶开始插花,共插b朵花,花只能插到无花的花瓶中,如果最后插不完b朵花,剩下的花舍弃掉
操作2:1 a b,把从编号a到编号b的所有花瓶里的花全部清理掉
对于操作1,需要输出开始插花的瓶子编号,和最后插花的瓶子编号
对于操作2,需要输出在a~b中总共清理了多少个花瓶中的花
做法:线段树 1表示没有插花,0表示插了花
对于操作二来说只用先查询区间和,再区间更改即可
对于操作一来说,要二分两次找出左右两边的端点,进行区间更改
(当时自己傻傻的写了一个单点更新,每次更新的时候先判断是否为1,然后与最大最小值比较,果断T)
#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>#define lson rt<<1,begin,mid#define rson rt<<1|1,mid+1,endusing namespace std;const int maxn=50001+100;int tree[maxn<<2];int laze[maxn<<2];void pushup(int rt){ tree[rt]=tree[rt<<1] + tree[rt<<1|1];}void pushdown(int rt,int k){ if(laze[rt]==1) { laze[rt<<1]=laze[rt<<1|1]=1; tree[rt<<1]=k-k/2; tree[rt<<1|1]=k/2; laze[rt]=-1; } else if(laze[rt]==0) { laze[rt<<1|1]=laze[rt<<1]=0; tree[rt<<1|1]=tree[rt<<1]=0; laze[rt]=-1; }}void build(int rt,int begin,int end){ tree[rt]=1; laze[rt]=-1; if(begin==end) return; int mid=(begin+end)>>1; build(lson); build(rson); pushup(rt);}void updata(int rt, int begin,int end ,int l,int r,int x){ if(begin>=l&&r>=end) { laze[rt]=x; tree[rt]=(end-begin+1)*x; return; } pushdown(rt,end-begin+1); int mid=(begin+end)>>1; if(mid>=l) updata(lson,l,r,x); if(r>mid) updata(rson,l,r,x); pushup(rt);}int query(int rt,int begin,int end,int l,int r){ if(begin>=l&& r>=end) { return tree[rt]; } pushdown(rt,end-begin+1); int ans=0; int mid=(begin+end)>>1; if(mid>=l) ans+=query(lson,l,r); if(r>mid) ans+=query(rson,l,r); pushup(rt); return ans;}int main(){ int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); build(1,1,n); while(m--) { int op,l,r; scanf("%d%d%d",&op,&l,&r); if(op==1) { ++l; int ans=query(1,1,n,l,n); if(ans==0) puts("Can not put any one."); else { if(ans<r) r=ans; int ll=l,rr=n; while(ll<=rr) { int mid=(ll+rr)>>1; if(query(1,1,n,l,mid)>=1) rr=mid-1; else ll=mid+1; } int a=ll; ll=l;rr=n; while(ll<=rr) { int mid=(ll+rr)>>1; if(query(1,1,n,l,mid)>=r) rr=mid-1; else ll=mid+1; } int b=ll; updata(1,1,n,a,b,0); printf("%d %d\n",a-1,b-1); } } else { ++r; ++l; printf("%d\n",r-l+1-query(1,1,n,l,r)); updata(1,1,n,l,r,1); } } printf("\n"); } return 0;}
阅读全文
0 0
- 2017暑假集训 div1 线段树(2)
- 2017暑假集训 div1 线段树(1)
- 2017暑假集训 div1 DP(2)
- 2017暑假集训 div1 最小生成树(1)
- 2017暑假集训 div1 最短路(2)
- 2017暑假集训 div1 并查集(2)
- 2017暑假集训 div1 连通图(2)
- 2017暑假集训 div1 DP(1)
- 2017暑假集训 div1 搜索进阶(1)
- 2017暑假集训 div1 最短路(1)
- 2017暑假集训 div1 最短路(3)
- 2017暑假集训 div1 并查集(1)
- 2017暑假集训 div1 连通图(1) POJ3694 &&POJ3177
- 2017暑假集训 div1 匹配问题(1)
- 2017暑假集训 div1 匹配问题(1)
- 2017暑假集训 div1 简单搜索
- 2017 暑假艾教集训 day11 线段树!
- 8.5 暑假集训——线段树篇
- linux常用命令总结
- python中lambda应用
- 设计六大原则纲要(二)
- label标签的用途
- Android开源数据库框架-LitePal的使用
- 2017暑假集训 div1 线段树(2)
- 使用a标签跳转Servlet并携带参数 给出弹窗
- "-"组合框(CComBox)综合文件.
- Ubantu硬盘分区
- 如何用export修改环境变量 以及 PATH与$PATH的区别
- 排序算法
- Azure上搭建站点到站点(Site to Site)的VPN
- Java数据结构
- 程序员上手mac os