CF 276C - Little Girl and Maximum Sum 差分数列,线段树,splay
题目here:
给出n个数,现在有m个区间询问l[i],r[i],问如何重新排列这n个数,使得
询问的和值最大
分析:
方法一:差分数列
一维的差分数列如下定义:
假设原数组为a[1]...a[n],a[0] = 0
差分数列数组为d[1]...d[n]
则d[i] = a[i]-a[i-1] (d数组初始化)
我们发现:a[i] = sigma(d[i])
所以我们对于区间[l,r]执行加同一个数的时候,我们可以执行
d[l] += val , d[r+1] -= val;
最终,我们可以用差分数列的累加和计算a[i]的值。
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define REP(i,a,b) for(int i=a;i<b;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back #define RD(n) scanf("%d",&n) string str; const int X = 2e5+5; int a[X],val[X]; int sum[X]; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); //freopen("sum.out","w",stdout); #endif int n,m; while(cin>>n>>m){ rep(i,n) scanf("%d",&val[i+1]); int x,y; memset(a,0,sizeof(a)); while(m--){ scanf("%d%d",&x,&y); a[x] ++; a[y+1] --; } for(int i=1;i<=n;i++) sum[i] = sum[i-1]+a[i]; sort(sum+1,sum+n+1); sort(val+1,val+n+1); ll ans = 0; REP(i,1,n+1) ans += (ll)val[i]*sum[i]; cout<<ans<<endl; } return 0; }
方法二: 线段树做法:区间更新,询问总区间就可以把所有的数存在数组中了
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define REP(i,a,b) for(int i=a;i<b;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back #define RD(n) scanf("%d",&n) string str; const int X = 2e5+5; ll a[X]; int val[X]; struct node{ int l,r; ll add; int mid(){ return (l+r)>>1; } }tree[X<<2]; void build(int l,int r,int rt){ tree[rt].l = l; tree[rt].r = r; tree[rt].add = 0; if(l==r) return; int mid = (l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); } void push_down(int rt){ tree[rt<<1].add += tree[rt].add; tree[rt<<1|1].add += tree[rt].add; tree[rt].add = 0; } void update(int l,int r,int rt){ if(tree[rt].l==l&&tree[rt].r==r){ tree[rt].add ++; return; } if(tree[rt].add) push_down(rt); int mid = tree[rt].mid(); if(r<=mid) update(l,r,rt<<1); else if(l>mid) update(l,r,rt<<1|1); else update(l,mid,rt<<1),update(mid+1,r,rt<<1|1); } void query(int l,int r,int rt){ if(tree[rt].l==tree[rt].r){ a[l] = tree[rt].add; return; } if(tree[rt].add) push_down(rt); int mid = tree[rt].mid(); if(l<=mid) query(tree[rt].l,mid,rt<<1); if(r>mid) query(mid+1,tree[rt].r,rt<<1|1); } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); //freopen("sum.out","w",stdout); #endif int n,m,x,y; while(cin>>n>>m){ rep(i,n) scanf("%d",&val[i+1]); build(1,n,1); while(m--){ scanf("%d%d",&x,&y); update(x,y,1); } query(1,n,1); sort(a+1,a+n+1); sort(val+1,val+n+1); ll ans = 0; rep(i,n) ans += a[i+1]*val[i+1]; cout<<ans<<endl; } return 0; }
splay简单区间操作,增加两个额外的节点,然后对于区间[a,b]执行加一操作时,把第a小的节点splay至根,把第b+2小的节点splay至根(增加了两个节点。。)。然后把根的右儿子的左儿子lazy标记加一。
/* splay简单区间操作,增加两个额外的节点,然后对于区间[a,b]执行加一操作时, 把第a小的节点splay至根,把第b+2小的节点splay至根(增加了两个节点。。) 然后把根的右儿子的左儿子lazy标记加一。 */ #include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define REP(i,a,b) for(int i=a;i<b;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back #define RD(n) scanf("%d",&n) string str; namespace Splay{ #define lx ch[x][0] #define rx ch[x][1] #define px pre[x] #define ly ch[y][0] #define ry ch[y][1] #define py pre[y] #define lz ch[z][0] #define rt ch[root][1] #define lrt ch[rt][0] const int MAXN = 2e5+5; int pre[MAXN],sz[MAXN],ch[MAXN][2]; ll sum[MAXN],add[MAXN]; int a[MAXN],n,m; int tot,root; inline void update(int x){ sz[x] = sz[lx]+sz[rx]+1; } inline void push_down(int x){ if(add[x]){ if(lx) add[lx] += add[x]; if(rx) add[rx] += add[x]; sum[x] += add[x]; add[x] = 0; } } inline int sgn(int x){ return ch[px][1]==x; } inline void setc(int y,int d,int x){ ch[y][d] = x; px = y; } inline void rot(int x,int d){ int y = px; int z = py; push_down(y); push_down(x); setc(y,!d,ch[x][d]); if(z) setc(z,sgn(y),x); pre[x] = z; setc(x,d,y); update(y); } inline void splay(int x,int goal=0){ push_down(x); while(px!=goal){ int y = px; int z = py; if(z==goal){ rot(x,!sgn(x)); break; } if(lz==y){ if(ly==x) rot(y,1),rot(x,1); else rot(x,0),rot(x,1); } else{ if(ry==x) rot(y,0),rot(x,0); else rot(x,1),rot(x,0); } } update(x); if(goal==0) root = x; } inline int get_Kth(int x,int k){ push_down(x); int tmp = sz[lx]+1; if(tmp==k) return x; return k<tmp?get_Kth(lx,k):get_Kth(rx,k-tmp); } inline void modify(int l,int r){ int x = get_Kth(root,l); int y = get_Kth(root,r+2); splay(x); splay(y,root); add[lrt] ++; update(rt); update(root); } inline void new_node(int &x,int y,ll v){ x = ++tot; sum[x] = v; add[x] = 0; lx = rx = 0; pre[x] = y; } inline void build(int &x,int y,int l,int r){ if(l>r) return; int mid = (l+r)>>1; new_node(x,y,0); build(lx,x,l,mid-1); build(rx,x,mid+1,r); update(x); } inline void dfs(int x){ if(x){ push_down(x); dfs(lx); dfs(rx); } } inline void init(){ memset(ch,0,sizeof(ch)); memset(pre,0,sizeof(pre)); memset(a,0,sizeof(a)); memset(sum,0,sizeof(sum)); memset(add,0,sizeof(add)); root = tot = 0; new_node(root,0,0); new_node(rt,root,0); update(rt); update(root); } void dfs_debug(int x){ if(x){ cout<<x<<" "<<lx<<" "<<rx<<endl; dfs_debug(lx); dfs_debug(rx); } } void solve(){ init(); int n,m; cin>>n>>m; build(lrt,rt,0,n-1); update(rt); update(root); //dfs_debug(root); rep(i,n) scanf("%d",&a[i]); int x,y; while(m--){ //debug; scanf("%d%d",&x,&y); modify(x,y); } dfs(root); // 把所有标记下沉 sort(sum+1,sum+tot+1); reverse(sum+1,sum+tot+1); sort(a,a+n); reverse(a,a+n); ll ans = 0; rep(i,n) ans += a[i]*sum[i+1]; cout<<ans<<endl; } }using namespace Splay; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); //freopen("sum.out","w",stdout); #endif solve(); return 0; }
HOJ 2332 // poj 2894 Ancient Keyboard 可以差分数列的方式做的