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 可以差分数列的方式做的

浙公网安备 33010602011771号