首页 > 其他分享 >Codeforces 1515I - Phoenix and Diamonds(值域倍增+线段树)

Codeforces 1515I - Phoenix and Diamonds(值域倍增+线段树)

时间:2023-06-01 20:12:18浏览次数:44  
标签:Phoenix int ll Codeforces MAXN Diamonds 物品 ord omega

首先 \(c\) 很大,因此复杂度跟 \(c\) 有关的项肯定只能是 \(\log c\) 之类的。

类比 IOI2021 dungeons 的套路,我们对值域进行分层,假设 \(c\in[2^{\omega-1},2^{\omega})\),考虑令重量在 \(\ge 2^{\omega-1}\) 的物品为“重物品”,其他物品为“轻物品”,那么一个显然的性质是我们最多只能取一个“重物品”。并且如果取了一个“重物品”,那么 \(c\) 就会进入 \(\omega\) 以下的层。如果存在一个“轻物品”,满足其不能取完全部 \(a_i\) 个,那么说明此时 \(c<w_i<2^{\omega-1}\),也会进入 \(\omega\) 以下的层。

考虑将这个过程放在线段树上。对线段树上每个节点 \([l,r]\) 维护以下三个信息:

  • \(lw_i\):\([l,r]\) 中所有重量 \(<2^{i-1}\) 的物品的 \(w_j·a_j\) 之和。
  • \(lv_i\):\([l,r]\) 中所有重量 \(<2^{i-1}\) 的物品的 \(v_j·a_j\) 之和。
  • \(tot_i\):要取走至少一个 \([l,r]\) 中重量在 \([2^{i-1},2^i)\) 中物品,\(c\) 至少为多少。

显然这些信息都可以在 \(O(\log V)\) 的时间内进行 pushup。具体细节见代码。

接下来怎么考虑查询。假设现在递归到 \([l,r]\) 表示的节点,\(c\in[2^{\omega},2^{\omega+1})\),特判掉以下两种情况:

  • \(c\ge lw_{\omega}\),说明所有可能能取走的物品都能取走,直接返回 \(lv_{\omega}\)。
  • \(lw_{\omega-1}\le c<tot_{\omega-1}\),说明所有轻物品都能取走,但重物品一个都不能取。直接返回 \(lv_{\omega-1}\)。

除此之外的所有情况都继续递归两个子节点。可以证明复杂度为 \(O(\log V\log n)\),这是因为对于第三种情况,要么你取走了一个重物品,要么遇到某个轻物品不能全部取完,根据前面的分析,此种情况都会使 \(c\) 至少减一,因此递归两个分叉的次数不会超过 \(\log V\)。

const int MAXN=2e5;
const int LOG_V=18;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,qu,w[MAXN+5],v[MAXN+5],ord[MAXN+5],pos[MAXN+5],omega;ll a[MAXN+5],c;
bool cmp(int x,int y){return ((v[x]==v[y])?(w[x]<w[y]):(v[x]>v[y]));}
struct node{int l,r;ll lw[LOG_V+2],lv[LOG_V+2],tot[LOG_V+2];}s[MAXN*4+5];
void pushup(int k){
	for(int i=1;i<=LOG_V;i++){
		s[k].lw[i]=s[k<<1].lw[i]+s[k<<1|1].lw[i];
		s[k].lv[i]=s[k<<1].lv[i]+s[k<<1|1].lv[i];
		s[k].tot[i]=min(s[k<<1].tot[i],s[k<<1|1].tot[i]+s[k<<1].lw[i]);
	}
}
void init_val(int k,int p){
	for(int i=1;i<=18;i++){
		s[k].lw[i]=s[k].lv[i]=0;s[k].tot[i]=INF;
		if(w[p]<(1<<i-1))s[k].lv[i]=1ll*v[p]*a[p],s[k].lw[i]=1ll*w[p]*a[p];
		else if(w[p]<(1<<i)&&a[p]>0)s[k].tot[i]=w[p];
	}
}
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;if(l==r)return init_val(k,ord[l]),void();
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void modify(int k,int p){
	if(s[k].l==s[k].r)return init_val(k,ord[p]),void();
	int mid=s[k].l+s[k].r>>1;
	if(p<=mid)modify(k<<1,p);else modify(k<<1|1,p);
	pushup(k);
}
void recalc(){while(omega>1&&(1<<((omega-1)-1))>c)omega--;}
ll query(int k){
	if(s[k].l==s[k].r){
		ll num=min(a[ord[s[k].l]],c/w[ord[s[k].l]]);
		c-=num*w[ord[s[k].l]];recalc();return num*v[ord[s[k].l]];
	}
	if(s[k].lw[omega]<=c){ll tmp=s[k].lv[omega];c-=s[k].lw[omega];recalc();return tmp;}
	else if(s[k].lw[omega-1]<=c&&c<s[k].tot[omega-1]){ll tmp=s[k].lv[omega-1];c-=s[k].lw[omega-1];recalc();return tmp;}
	else return query(k<<1)+query(k<<1|1);
}
int main(){
	scanf("%d%d",&n,&qu);
	for(int i=1;i<=n;i++)scanf("%lld%d%d",&a[i],&w[i],&v[i]),ord[i]=i;
	sort(ord+1,ord+n+1,cmp);for(int i=1;i<=n;i++)pos[ord[i]]=i;
	build(1,1,n);
	while(qu--){
		int opt;scanf("%d",&opt);
		if(opt==1||opt==2){
			int k,d;scanf("%d%d",&k,&d);
			a[d]+=((opt==1)?k:(-k));
			modify(1,pos[d]);
		}else{
			scanf("%lld",&c);omega=18;recalc();
			printf("%lld\n",query(1));
		}
	}
	return 0;
}

标签:Phoenix,int,ll,Codeforces,MAXN,Diamonds,物品,ord,omega
From: https://www.cnblogs.com/tzcwk/p/Codeforces-1515I.html

相关文章

  • Codeforces Beta Round #2--B题 (DP)
    题目:Theleastroundway 1000*1000的方阵,每个格子有一个非负整数,现在要从左上走到右下,每次只能向下或者向右走。目标是使得所有走的格子里的数的乘积里,末尾0的个数最少,要求输出最有解和走法。不用怎么想也知道基本是个dp了,可以发现其实只有2和5的因子是有用的,但是如果状态同时记......
  • Codeforces Round 875 (Div. 2)B-D
    原题链接:https://codeforces.com/contest/1831原文:https://www.cnblogs.com/edgrass/p/17440602.html(B)Arraymerging主体思想是找到ab数组的最长相同字串(c中操作可实现连续)1#include<bits/stdc++.h>2usingnamespacestd;3intmain(){4intt;5cin>>......
  • Codeforces Round #548 (Div. 2) 题解
    题目链接http://codeforces.com/contest/1139 A.EvenSubstrings判断个位是否是奇数即可。#include<iostream>#include<set>#include<array>#include<vector>usingnamespacestd;typedeflonglongll;constintmx=1e5+10;lldp[mx][2];charstr[......
  • CodeForces 1250E [2-sat]
    题目链接:https://codeforces.com/contest/1250/problem/E 解题思路:首先根据反转的性质有:    有字符串a,b,a的反转A,b的反转B,如果a和b匹配值为K,那么A和B的值也为K    同样a和B匹配值为M,那么A和b的匹配值也为M,因此有对称性那么就可以转换成2-sat问题了,设点i的对立......
  • Codeforces Round #594 (Div. 2) 题解
    题目链接:https://codeforces.com/contest/1248 A-IntegerPoints水题#include<bits/stdc++.h>usingnamespacestd;constintmx=1e5+5;typedeflonglongll;inta[mx],b[mx];intmain(){ intt; scanf("%d",&t); while(t--){ intn,m; scan......
  • CodeForcese 1256F [思维]
    题目链接:https://codeforces.com/problemset/problem/1256/F 解题思路:任意的翻转都可以认为是翻转若干个长度为2的,交换两个数主要奇数次翻转。两个串可以翻转成一样,那么一定可以再花费一样的翻转次数变成有序的字符串,连续两个相等的字符翻转任意数次是一样的,所以只要有两个字符......
  • Codeforces Round #599 (Div. 2) 题解
    题目链接:https://codeforces.com/contest/1243 A-MaximumSquare水题不说了#include<bits/stdc++.h>usingnamespacestd;constintmx=1e5+50;typedeflonglongll;intn;inta[mx];intmain(){ intt; scanf("%d",&t); while(t--){ scan......
  • Codeforces #564 (Div. 2) C. Nauuo and Cards[贪心]
    题目链接:http://codeforces.com/contest/1173/problem/C 解题思路:很明显总的抽卡次数是不会超过2*n的。然后我们再将情况分成两种:1.编号1的卡片已经在里面了,并且最底部已经形成了一个1~x的连续的卡片,而且之后x+1,x+2都可以来得及补上,在这种情况下抽卡次数肯定就不会超过n了。2.......
  • Codeforces #564 (Div. 2) D. Nauuo and Circle[树形DP]
    题目链接:http://codeforces.com/contest/1173/problem/D 解题思路:首先得知道按照圆周顺序的话,那么一颗子树必须存放在连续的一段区间里面,现在我们假设根节点是1,那么一颗为u做根节点的子树他的方案数就是各个儿子的方案数的乘积最后再乘以儿子个数+1(u节点本身)的排列方案数。所......
  • Educational Codeforces Round 149 (Rated for Div. 2)
    A.GrasshopperonaLine#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){intx,k;cin>>x>>k;if(x%k==0){cout<<"2\n"<<x-1<<"&......