UESTC数据结构专题训练 G,H,I,J
2017-06-08 16:20
302 查看
G,http://acm.uestc.edu.cn/#/problem/show/1598
题意:给你一个n个元素的序列,支持以下操作:单点修改,查询区间内连续的最大子段和。
解法:线段树每个结点维护四个域:maxv,maxl,maxr,sumv,其中sumv为该区间的和,maxv为该区间上的最大子段和,maxl为必须包含左端点的最大子段和,maxr为必须包含右端点的最大子段和。之后我们用lc和rc表示左子节点和右子节点。
修改时:
1、若 lc.maxr 和 rc.maxl 都为负,就从中取较大的为该节点的maxv(防止一个都不取),反之取二者中正的(都正就都取)。
2、将该节点的 maxv 用 lc 和 rc 的 maxv 更新。
3、该节点的 maxl 为 max { lc.maxl , lc.sumv + rc.maxl }。
4、该节点的 maxr 为 max { rc.maxr , rc.sumv + lc.maxr }。
5、该节点的 sumv 为 lc.sumv + rc.sumv 。
查询时:
按照修改的方式将询问区间覆盖的结点信息合并即可。
H,http://acm.uestc.edu.cn/#/problem/show/1581
题意:给你一个n个元素的序列,支持以下操作:单点修改,查询某个下标是等差数列的子序列的最小值
解法:乱搞题。设置一个阈值K,
当公差>K的时候,可以暴力计算答案。我这里设成5就过了。
当公差小于K的时候,对每一个公差d,及其所有小于等于d的首项,建立一棵线段树。注意,公差确定时,每个首项对应的线段树是没有交集的。
共O(K2)棵线段树,总空间为O(K*n)。
询问时,找到它究竟在哪棵线段树。
修改时,在K棵线段树中进行修改。
I,http://acm.uestc.edu.cn/#/problem/show/1603
题意:现在有一个序列 a1,a2,...,an
,相应将其分成若干个 heapable 的子序列,问使得子序列个数最少的策略。
解法如下:
用set维护,set中存储的是个pair第一维权值,第二维是在原序列的下标。
贪心地从前往后扫,每到一个元素,就查找之前的元素中小于等于其的最大的元素//it=Set.lower_bound(pair<a[i],i>);it--;//,如果存在,就将它置为其父亲。如果一个结点已经是两个儿子的父亲了,就不能在set中存在了,就将他删除。如果然后将当前元素插入。
pair的比较函数是双关键字比较,先按第一关键字,如果相同,再按第二关键字。
如果不存在,就直接将当前元素插入。
证明是比较显然的,因为对于某个元素,前面各个堆中权值相同的结点都是等价的。
输出答案时用邻接表/*vector<int>G
;*/把树存下来,跑个dfs,然后排个序输出就行。
复杂度:O(n*log(n)*log(n))
J,http://acm.uestc.edu.cn/#/problem/show/1599
题意:给你n个数,你每次可以合并两个数,合并所得的数是原来两个数之和,合并的代价也是原来两个数之和。n-1次合并后只剩一个数,问你该过程的最小代价。
解法:贪心 用堆来维护,将所有元素放进堆。 每次取出当前最小的两个元素,将其合并,统计答案,放回堆。重复n-1次。比较显然,先合并小的,再合并大的明显更优。
题意:给你一个n个元素的序列,支持以下操作:单点修改,查询区间内连续的最大子段和。
解法:线段树每个结点维护四个域:maxv,maxl,maxr,sumv,其中sumv为该区间的和,maxv为该区间上的最大子段和,maxl为必须包含左端点的最大子段和,maxr为必须包含右端点的最大子段和。之后我们用lc和rc表示左子节点和右子节点。
修改时:
1、若 lc.maxr 和 rc.maxl 都为负,就从中取较大的为该节点的maxv(防止一个都不取),反之取二者中正的(都正就都取)。
2、将该节点的 maxv 用 lc 和 rc 的 maxv 更新。
3、该节点的 maxl 为 max { lc.maxl , lc.sumv + rc.maxl }。
4、该节点的 maxr 为 max { rc.maxr , rc.sumv + lc.maxr }。
5、该节点的 sumv 为 lc.sumv + rc.sumv 。
查询时:
按照修改的方式将询问区间覆盖的结点信息合并即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 500010; struct node{ int l,r,lm,rm,mx,sum; }tree[maxn<<2]; int n,m,a[maxn]; void pushup(int rt){ tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; tree[rt].lm=max(tree[rt<<1].lm,tree[rt<<1].sum+tree[rt<<1|1].lm); tree[rt].rm=max(tree[rt<<1|1].rm,tree[rt<<1].rm+tree[rt<<1|1].sum); tree[rt].mx=max(max(tree[rt<<1].mx,tree[rt<<1|1].mx),tree[rt<<1].rm+tree[rt<<1|1].lm); } void build(int l, int r, int rt){ tree[rt].l=l,tree[rt].r=r; if(l==r){ tree[rt].sum=tree[rt].lm=tree[rt].rm=tree[rt].mx=a[l]; return; } int mid=(l+r)/2; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); pushup(rt); } void update(int pos, int val, int rt){ if(tree[rt].l==tree[rt].r){ tree[rt].sum=tree[rt].lm=tree[rt].rm=tree[rt].mx=val; return; } int mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) update(pos,val,rt<<1); else update(pos,val,rt<<1|1); pushup(rt); } node query(int L, int R, int rt){ if(L<=tree[rt].l&&tree[rt].r<=R){ return tree[rt]; } int mid=(tree[rt].l+tree[rt].r)/2; if(R<=mid) return query(L,R,rt<<1); else if(L>mid) return query(L,R,rt<<1|1); else{ node a,g,h; g=query(L,mid,rt<<1); h=query(mid+1,R,rt<<1|1); a.mx=max(max(g.mx,h.mx),g.rm+h.lm); a.rm=max(h.rm,g.rm+h.sum); a.lm=max(g.lm,g.sum+h.lm); return a; } } int main() { int n, m; while(~scanf("%d%d",&n,&m)){ for(int i=1; i<=n; i++) scanf("%d", &a[i]); build(1,n,1); while(m--){ int op,l,r; scanf("%d %d %d", &op, &l, &r); if(op==1){ printf("%d\n", query(l,r,1).mx); } else{ update(l,r,1); } } } return 0; }
H,http://acm.uestc.edu.cn/#/problem/show/1581
题意:给你一个n个元素的序列,支持以下操作:单点修改,查询某个下标是等差数列的子序列的最小值
解法:乱搞题。设置一个阈值K,
当公差>K的时候,可以暴力计算答案。我这里设成5就过了。
当公差小于K的时候,对每一个公差d,及其所有小于等于d的首项,建立一棵线段树。注意,公差确定时,每个首项对应的线段树是没有交集的。
共O(K2)棵线段树,总空间为O(K*n)。
询问时,找到它究竟在哪棵线段树。
修改时,在K棵线段树中进行修改。
#include <bits/stdc++.h> using namespace std; const int maxn=70010; int n,m,a[maxn],pos1[6][maxn],pos2[6][maxn]; struct seg{ int c, tree[maxn<<2]; void pushup(int rt){ tree[rt]=max(tree[rt<<1],tree[rt<<1|1]); } void build(int l, int r, int rt){ if(l==r){ tree[rt]=a[pos2[c][l]]; return; } int mid=(l+r)/2; build(l,mid,rt*2); build(mid+1,r,rt*2+1); pushup(rt); } void update(int pos, int l, int r, int rt){ if(l==r){ tree[rt]=a[pos2[c][l]]; return; } int mid=(l+r)>>1; if(pos<=mid) update(pos,l,mid,rt<<1); else update(pos,mid+1,r,rt<<1|1); pushup(rt); } int query(int L, int R, int l, int r, int rt){ if(L<=l&&r<=R){ return tree[rt]; } int mid=(l+r)>>1; if(R<=mid) return query(L,R,l,mid,rt<<1); else if(L>mid) return query(L,R,mid+1,r,rt<<1|1); else{ return max(query(L,mid,l,mid,rt<<1),query(mid+1,R,mid+1,r,rt<<1|1)); } } }T[6]; int main() { while(~scanf("%d", &n)){ for(int i=1; i<=n; i++) scanf("%d", &a[i]); for(int i=1; i<=5; i++){ int now=1,tot=1; for(int j=1; j<=n; j++){ pos1[i][now]=j; pos2[i][j]=now; now+=i; if(now>n) now=++tot; } T[i].c=i; T[i].build(1,n,1); } scanf("%d", &m); while(m--){ int op,x,y; scanf("%d %d %d",&op,&x,&y); if(op==0){ a[x]+=y; for(int i=1; i<=5; i++){ T[i].update(pos1[i][x],1,n,1); } } else{ if(y<=5){ int r=pos1[y][x]+(n-x)/y; printf("%d\n", T[y].query(pos1[y][x],r,1,n,1)); } else{ int ans=a[x]; for(int i=x; i<=n; i+=y){ ans=max(ans,a[i]); } printf("%d\n",ans); } } } } return 0; }
I,http://acm.uestc.edu.cn/#/problem/show/1603
题意:现在有一个序列 a1,a2,...,an
,相应将其分成若干个 heapable 的子序列,问使得子序列个数最少的策略。
解法如下:
用set维护,set中存储的是个pair第一维权值,第二维是在原序列的下标。
贪心地从前往后扫,每到一个元素,就查找之前的元素中小于等于其的最大的元素//it=Set.lower_bound(pair<a[i],i>);it--;//,如果存在,就将它置为其父亲。如果一个结点已经是两个儿子的父亲了,就不能在set中存在了,就将他删除。如果然后将当前元素插入。
pair的比较函数是双关键字比较,先按第一关键字,如果相同,再按第二关键字。
如果不存在,就直接将当前元素插入。
证明是比较显然的,因为对于某个元素,前面各个堆中权值相同的结点都是等价的。
输出答案时用邻接表/*vector<int>G
;*/把树存下来,跑个dfs,然后排个序输出就行。
复杂度:O(n*log(n)*log(n))
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; int n, cnt, a[maxn], son[maxn]; vector <int> G[maxn]; struct node{ int first,second; }; bool operator<(node x, node y){ if(a[x.second]==a[y.second]) return x.second<y.second; return a[x.second]<a[y.second]; } set<node>s; int main() { int T; scanf("%d",&T); while(T--){ s.clear(); cnt=0; scanf("%d", &n); memset(son,0,sizeof(son)); //for(int i=0; i<maxn; i++) G[i].clear(); for(int i=1; i<=n; i++){ scanf("%d",&a[i]); node now; now.second=i; set<node>::iterator it=s.upper_bound(now); if(it==s.begin()){ G[cnt].push_back(i); now.first=cnt,now.second=i; s.insert(now); cnt++; } else{ it--; now=*it; son[now.second]++; if(son[now.second]==2) s.erase(it); now.second=i; s.insert(now); G[now.first].push_back(i); } } printf("%d\n", cnt); for(int i=0; i<cnt; i++){ printf("%d",G[i].size()); for(int j=0; j<G[i].size(); j++){ printf(" %d", G[i][j]); } printf("\n"); G[i].clear(); } } return 0; }
J,http://acm.uestc.edu.cn/#/problem/show/1599
题意:给你n个数,你每次可以合并两个数,合并所得的数是原来两个数之和,合并的代价也是原来两个数之和。n-1次合并后只剩一个数,问你该过程的最小代价。
解法:贪心 用堆来维护,将所有元素放进堆。 每次取出当前最小的两个元素,将其合并,统计答案,放回堆。重复n-1次。比较显然,先合并小的,再合并大的明显更优。
#include <bits/stdc++.h> using namespace std; priority_queue<int,vector<int>,greater<int> >q; int n, x, ans; int main() { while(~scanf("%d", &n)){ ans=0; while(!q.empty()) q.pop(); for(int i=1; i<=n; i++){ scanf("%d", &x); q.push(x); } while(q.size()>1){ int cur = q.top(); q.pop(); int cur1 = q.top(); q.pop(); ans += cur+cur1; q.push(cur+cur1); } printf("%d\n", ans); } return 0; }
相关文章推荐
- UESTC 数据结构专题训练 A,B,C
- 【线段树+离散化】Uestc-数据结构专题训练【B】
- 【裸线段树】Uestc-数据结构专题训练【A】
- UESTC 数据结构专题训练 D,E,F
- UESTC 数据结构专题训练 K,L,M
- 2015 UESTC 数据结构专题A题 秋实大哥与小朋友 线段树 区间更新,单点查询,离散化
- 2014 UESTC暑前集训数据结构专题解题报告
- 2015 UESTC 数据结构专题B题 秋实大哥与花 线段树 区间加,区间查询和
- 2015 UESTC 数据结构专题C题 秋实大哥与快餐店 字典树
- 2015 UESTC 数据结构专题D题 秋实大哥与战争 变化版本的线段树,合并区间,单点查询
- UESTC专题训练
- 2015 UESTC 数据结构专题D题 秋实大哥与战争 SET的妙用
- 数据结构专题训练及总结
- 2015 UESTC 数据结构专题E题 秋实大哥与家 线段树扫描线求矩形面积交
- 【暑假专题训练#数据结构】
- 暑假训练4-数据结构专题
- 2015 UESTC 数据结构专题G题 秋实大哥去打工 单调栈
- 数据结构专题训练一 C Bits
- 2015 UESTC 数据结构专题H题 秋实大哥打游戏 带权并查集
- 2015 UESTC 数据结构专题N题 秋实大哥搞算数 表达式求值/栈