zoukankan      html  css  js  c++  java
  • [CTSC2018]混合果汁

    V.III.[CTSC2018]混合果汁

    二话不说先套个整体二分。

    但是这题整体二分与先前两道题有所区别——前面两道题,当二分到区间 \([l,r]\) 时,只需管 \([l,r]\) 中的元素就行了,对于 \(mid\) 不合法的询问直接减去这一段的询问的结果就行了;

    但是,本题就不一样了:随着美味度下界的不断降低,我们可能会放弃贵但美味的饮料,转而选择便宜但不美味的饮料。这就意味着上面的做法不太行。

    但是,我们是有办法的:仿照CDQ分治的思想,当处理区间 \([l,r]\) 结束后,我们首先处理 \([mid,r]\),等到遍历到叶子节点时将其代表的元素加入BIT,等 \([mid,r]\) 全数处理完成后再处理 \([l,mid]\)。这样子,在处理任何 \([l,r]\) 时,\([r+1,n]\) 便已经在BIT里了。

    至于这个BIT是用来干嘛的呢,你可以在上面二分出要买几升果汁最少花多少钱。

    时间复杂度 \(O(n\log^2n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1<<17;
    int n,m,p[N+10],res[N+10];
    ll mon[N+10],wat[N+10],lit[N+10],pay[N+10],all;
    bool ok[N+10];
    vector<pair<int,int> >v[N+10];
    void ADD(int pri,int amt){all+=amt;for(int i=pri;i<=N;i+=i&-i)lit[i]+=amt,pay[i]+=1ll*pri*amt;}//price,amount
    ll ASK(ll amt){
    	int i=N;ll cst=0;
    //	printf("%lld:\n",amt);
    	for(int j=17;j>=0;j--){
    		if(i<(1<<j))continue;
    //		printf("%d:%d %d:%d,%d %d\n",i,j,i-(1<<j),lit[i-(1<<j)],pay[i-(1<<j)],amt);
    		if(lit[i-(1<<j)]>=amt)i-=(1<<j);else amt-=lit[i-(1<<j)],cst+=pay[i-(1<<j)];
    	}
    	cst+=i*amt;
    //	printf("%lld,%d,%lld\n",amt,i,cst);
    	return cst;
    }
    void solve(int l,int r,int L,int R){
    	if(l==r){
    //		printf("[%d]:",l);for(int i=L;i<=R;i++)printf("%d ",p[i]);puts("");
    		for(int i=L;i<=R;i++)res[p[i]]=l;
    		for(auto i:v[l])ADD(i.first,i.second);
    		return;
    	}
    	int mid=(l+r+1)>>1;
    	for(int i=mid;i<=r;i++)for(auto j:v[i])ADD(j.first,j.second);
    //	printf("%lld:[%d|%d|%d]:",all,l,mid,r);for(int i=L;i<=R;i++)printf("%d ",p[i]);puts("");
    	for(int i=L;i<=R;i++)ok[p[i]]=(all>=wat[p[i]]&&ASK(wat[p[i]])<=mon[p[i]]);
    	for(int i=mid;i<=r;i++)for(auto j:v[i])ADD(j.first,-j.second);
    	sort(p+L,p+R+1,[](int x,int y){return ok[x]>ok[y];});
    	int MID=L-1;while(MID<R&&ok[p[MID+1]])MID++;
    	solve(mid,r,L,MID),solve(l,mid-1,MID+1,R);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y,z;i<=n;i++)scanf("%d%d%d",&x,&y,&z),v[x].push_back(make_pair(y,z));
    	for(int i=1;i<=m;i++)scanf("%lld%lld",&mon[i],&wat[i]),p[i]=i;
    	solve(0,N,1,m);
    	for(int i=1;i<=m;i++)printf("%d\n",!res[i]?-1:res[i]);
    	return 0;
    }
    
  • 相关阅读:
    回溯法 | 子集树:装载问题
    再谈01背包 | 使用【跳跃点集合】代替【求解矩阵】解题
    线性代数 | 行列式的计算
    慢,也是一种态度
    回溯法 | 图的m着色问题
    回溯法 | 旅行商问题(TSP问题)
    回溯法 | n皇后问题
    基数排序 RadixSort
    03.父工程pom、整合测试、SpringBootApplication注解
    02.自定义banner、全局配置文件、@Value获取自定义配置、@ConfigurationProperties、profiles配置
  • 原文地址:https://www.cnblogs.com/Troverld/p/14620869.html
Copyright © 2011-2022 走看看