2017暑假集训 div1 线段树(2)
2017-07-14 10:22
543 查看
HDU 4027
题意:给一串数字,两种操作,操作1:区间所有数字开根号,操作二:求区间和
做法:线段树即可,但进行操作1 时先判断一下是否区间和为它的长度,如果是就可以不用更改了(没有这个会T)
HDU 1540
题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少
做法:正解是线段树的合并!!!
但其实stl set判断当前点左右两边被摧毁的距离也可以(注意加入0,和n+1)边界
HDU 3974
题意:
一个公司里面每个员工都有一个顶头上司,一旦给某个员工分配任务后,这个员工以及该员工的所有下属都在做该任务。
有若干操作,分配给员工任务以及查询该员工正在执行的任务。
做法:首先线段树是很容易想到的,那关键是怎么在树上跑线段树,其实我们可以先用出入度找出树的根,然后DFS找出dfs序,一个点两次出现的中间点都是该点的子孙!!!
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
HDU 4614
现在要你插花,有n个花瓶,m次操作,初始花瓶中无花,操作有两种方式
操作1:1 a b,从编号为a的花瓶开始插花,共插b朵花,花只能插到无花的花瓶中,如果最后插不完b朵花,剩下的花舍弃掉
操作2:1 a b,把从编号a到编号b的所有花瓶里的花全部清理掉
对于操作1,需要输出开始插花的瓶子编号,和最后插花的瓶子编号
对于操作2,需要输出在a~b中总共清理了多少个花瓶中的花
做法:线段树 1表示没有插花,0表示插了花
对于操作二来说只用先查询区间和,再区间更改即可
对于操作一来说,要二分两次找出左右两边的端点,进行区间更改
(当时自己傻傻的写了一个单点更新,每次更新的时候先判断是否为1,然后与最大最小值比较,果断T)
题意:给一串数字,两种操作,操作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,end using 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,end const 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 ,end using 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 4000 ; 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表示没有插花,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,end using 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; }
相关文章推荐
- 2017暑假集训 div1 连通图(1) POJ3694 &&POJ3177
- 2017暑假集训 div1 DP(1)
- 2017暑假集训 div1 连通图(2)
- 2017暑假集训 div1 并查集(1)
- 2017暑假集训 div1 最小生成树(1)
- 2017暑假集训 div1 匹配问题(1)
- 2017暑假集训 div1 匹配问题(1)
- 2017 暑假艾教集训 day11 线段树!
- 2017暑假集训 div1 最短路(1)
- 2017暑假集训 div1 最短路(2)
- 2017暑假集训 div1 并查集(2)
- 2017暑假集训 div1 最短路(3)
- LOJ.6062.[2017山东一轮集训]Pair(Hall定理 线段树)
- [ 后缀自动机 树上启发式合并 线段树 树状数组 ] [ 雅礼集训 2017 Day7 ] LOJ#6041
- 2017暑假集训第三天
- 2017 暑假艾教集训 day7 (树链剖分模板)
- 2017 暑假艾教集训 day9(整体二分 + cdq分治 cdq真是我女神!!!)
- 2017暑假集训 div1 简单搜索
- 2017暑假集训总结
- 2017暑假集训感悟