codeforces 1070(差分||权值线段树)

https://codeforces.com/contest/1070/problem/C
题意:
m m m种借用云计算内核的方案,第i种方案 l i   r i   c i   p i li \ ri\ ci\ pi li ri ci pi表示,表示这种方案的可借用时间是从第li 到 ri 天,数量有 ci价格 为pi。公司在1~n 天内如果能租到k件及以上,选择最小价格的k件,否则全部租下,问最后总的价格

思路:由于选最小价值的k件物品,考虑用权值线段树来维护,每天的更新直接用差分来更新,第li天插入,ri+1天减去即可,根据权值线段树的二分性质,权值从小到大的建树方式,所以递归寻找最左边的k件即可,如果小于k件,就全部累加

#include
#include
#define fi first
#define se second
#define show(a) cout<
#define show2(a,b) cout<
#define show3(a,b,c) cout<
using namespace std;
 
typedef long long ll;
typedef pair<int, int> P;
typedef pair<P, int> LP;
const ll inf = 0x3f3f3f3f;
const int N = 1e6 + 10;
const ll mod = 10007;
const int base=131;
tr1::unordered_map<ll,ll> mp;
 
ll n,m,id,x,y,k,q,cnt,flag;
ll num[N<<2];//记录数量
ll sum[N<<2];//记录总价值
ll ans;
int c[N],p[N];
vector<P> v[N];
 
void pushup(int rt)
{
	num[rt]=num[rt<<1]+num[rt<<1|1];
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(ll c,ll p,int l,int r,int rt)//更新就是单点更新P的值
{
	if(l==r)
	{
		num[rt]+=c;
		sum[rt]+=c*p;
		//show3(l,num[rt],sum[rt])
		return ;
	}
	int mid=l+r >> 1;
	if(p<=mid) update(c,p,l,mid,rt<<1);
	else update(c,p,mid+1,r,rt<<1|1);
	pushup(rt);
}
ll query(ll k,int l,int r,int rt)
{
	if(l==r)
	{
		return min(k,num[rt])*l;//某些节点可能为空 即总数小于k的情况 这里l即为价格
	}
	int mid=l+r>>1;
	if(num[rt<<1]>=k) return query(k,l,mid,rt<<1);//如果左子树数量大于k,直接进入左子树寻找
	else return sum[rt<<1]+query(k-num[rt<<1],mid+1,r,rt<<1|1);//否则左子树贡献+右子树需要数量的贡献
}
int main() {
 
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
 
	cin>>n>>k>>m;
	for(int i=1;i<=m;i++)
	{
		int l,r;
		cin>>l>>r;
		cin>>c[i]>>p[i];
		v[l].push_back(P(c[i],p[i]));
		v[r+1].push_back(P(-c[i],p[i]));
	}
	for(int i=1;i<=N;i++)
	{
		for(int j=0;j<v[i].size();j++)
		{
			update(v[i][j].fi,v[i][j].se,1,N,1);
		}
 
		ans+=query(k,1,N,1);
		//show2(i,ans)
	}
	cout<<ans<<endl;
 
}

你可能感兴趣的:(线段树)