首页 > 其他分享 >【CF1753E】N Machines(暴力+二分)

【CF1753E】N Machines(暴力+二分)

时间:2022-10-26 18:44:31浏览次数:106  
标签:二分 移动 int lim CF1753E 操作 乘法 Machines define

题目链接

  • 给定一个操作序列,包含 \((+,a_i),(\times,a_i)\) 两种操作。
  • 初始 \(x=1\),会从左到右依次执行所有操作得到一个终值 \(x'\)。
  • 共有 \(lim\) 块钱,可以花 \(p1\) 块钱任意移动一个加法操作,或花 \(p2\) 块钱任意移动一个乘法操作。
  • 求 \(x'\) 的最大值。
  • \(1\le n\le10^6\),保证初始的 \(x'\le2\times10^9\)

枚举乘法操作

首先去掉所有 \((*,1)\) 的操作,显然这些操作没有任何影响。

同时移动 \((+,a_i),(\times,a_i)\),它们之间的影响比较复杂,不太能直接做。

考虑初始 \(x'\le2\times10^9\),乘法操作数量其实非常小。因此我们尝试先枚举移动的乘法操作,然后再去移动加法操作。

注意到对于值相同的乘法操作,移动前面的若干个肯定优于移动后面的。

所以假设有 \(c_i\) 个值为 \(i\) 的乘法操作,枚举的复杂度应当是 \(\prod(c_i+1)\)。而 \(\prod i^{c_i}\le2\times10^9\),这里的复杂度并不会很大。

处理加法操作

确定了乘法操作的移动后,移动每个加法操作的价值就能确定下来了。假设还能移动 \(k\) 个加法操作,则我们就是要求前 \(k\) 大的价值之和。

比赛时比较 sb,直接暴力 \(O(n)\) 做这个东西,T 飞了。

实际上,考虑乘法操作至多只有 \(\log V\) 个,而两个乘法操作之间的加法操作顺序是没有影响的,移动较大的肯定优于移动小的。

于是我们先二分第 \(k\) 大的价值,然后在每个段中二分求个数检验即可。

每次的复杂度应该是三只 \(\log\)。

代码

#include<bits/stdc++.h>
#define Cn const
#define CI Cn int&
#define N 1000000
#define LL long long
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define Tp template<typename Ty>
	#define Ts template<typename Ty,typename... Ar>
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp void read(Ty& x) {x=0;while(!isdigit(oc=tc()));while(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts void read(Ty& x,Ar&... y) {read(x),read(y...);}
	void readc(char& x) {while(isspace(x=tc()));}
}using namespace FastIO;
int n,c,lim,tot,p1,p2,s[N+5],v[N+5],h[N+5],dc,dv[N+5];vector<int> w[N+5],W[N+5],g[N+5];LL ans;
int f[N+5];void Calc(int k)//处理加法操作
{
	int i,j,z,t=1;LL res=0;for(i=1;i<=c;++i) res+=1LL*s[i]*(tot/t),f[i]=(t-1)*(tot/t),!h[i]&&(t*=dv[v[i]]);//计算每段的权
	LL r=0;for(i=1;i<=c;++i) !w[i].empty()&&(r=max(r,1LL*w[i].back()*f[i]));
	LL l=0,u,tar;int ct,ll,rr,uu;while(l<r)//二分第k大值
	{
		for(u=l+r+1>>1,ct=0,i=1;i<=c;++i)//求个数检验
		{
			if(w[i].empty()||1LL*w[i].back()*f[i]<u) continue;tar=(u+f[i]-1)/f[i];
			z=w[i].size(),ll=0,rr=z-1;while(ll^rr) w[i][uu=ll+rr-1>>1]>=tar?rr=uu:ll=uu+1;ct+=z-rr;//二分
		}ct>=k?l=u:r=u-1;
	}
	for(ct=0,i=1;i<=c;++i)//求前k大之和
	{
		if(w[i].empty()||1LL*w[i].back()*f[i]<=l) continue;tar=l/f[i]+1;
		z=w[i].size(),ll=0,rr=z-1;while(ll^rr) w[i][uu=ll+rr-1>>1]>=tar?rr=uu:ll=uu+1;ct+=z-rr,res+=1LL*W[i][rr]*f[i];//二分
	}
	ans=max(ans,res+(k-ct)*l);
}
void dfs(int x,int lim)//枚举乘法操作
{
	if(x>dc) return Calc(lim/p1);dfs(x+1,lim);
	for(vector<int>::iterator it=g[x].begin();it!=g[x].end()&&lim>=p2;++it) h[*it]=1,lim-=p2,dfs(x+1,lim);//对每种值移动前面的若干个
	for(vector<int>::iterator it=g[x].begin();it!=g[x].end();++it) h[*it]=0;
}
int main()
{
	int i,j,x,z;char op;read(n,lim,p1,p2);
	for(c=tot=i=1;i<=n;++i) readc(op),read(x),op=='*'?x^1&&(v[c++]=x,tot*=x):(w[c].push_back(x),s[c]+=x);v[c]=1;//注意删去乘1的操作
	for(i=1;i<=c;++i) for(sort(w[i].begin(),w[i].end()),z=w[i].size(),W[i]=w[i],j=z-2;j>=0;--j) W[i][j]+=W[i][j+1];
	for(i=1;i<=c;++i) dv[i]=v[i];sort(dv+1,dv+c+1),dc=unique(dv+1,dv+c+1)-dv-1;
	for(i=1;i<=c;++i) g[v[i]=lower_bound(dv+1,dv+dc+1,v[i])-dv].push_back(i);return dfs(2,lim),printf("%lld\n",ans+tot),0;
}

标签:二分,移动,int,lim,CF1753E,操作,乘法,Machines,define
From: https://www.cnblogs.com/chenxiaoran666/p/CF1753E.html

相关文章

  • 循环不变量,双指针,精准定义| 代码随想录算法训练营第一天| 704. 二分查找、27. 移除
    收获:抓住循环不变量双指针入门,学会精准定义目录704思路错误解法解题方法Code27思路解题方法Code704Problem:704.二分查找思路讲述看到这一题的思路思......
  • CF1753E N Machines
    题面传送门首先你发现题面里有一个初始答案不大于\(2\times10^9\),这表示最终答案不超过\(4\times10^{18}\),这表明不用写高精,这是好的。但是这仅仅如此吗?可以发现乘\(1......
  • ACWing 4480 -- 二分&&双指针&&思维
    题目描述倒垃圾思路其实就是思维题,题意很简单,找到一个居民左侧和右侧(如果存在的话)的垃圾桶中最近的那个垃圾桶,然后让那个垃圾桶的计数器加一。从题目中挖掘的性质:左......
  • 二分+字符串哈希
    字符串哈希先说一维字符串哈希。基本思想是对每个\(i\),先求出\([1,i]\)上字符串哈希的值(前缀和思想),然后使用类似差分的方法求出\([x,y]\)上字符串哈希的值。具体算......
  • 二分查找法与函数
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>         //这里的arr本质上是一个指针intbinary_search(intarr[],intk,intsz){  ......
  • POJ 3122(二分答案)
    二分答案……Programpie;constlef=0.00001;vart,n,f,i,j:longint;r:array[1..10000]oflongint;s:array[1..10000]ofdouble;maxs:double;procedures......
  • 分割矩阵 (二分范围[L,R))
       分割矩阵            (browine.c/cpp/pas)【问题描述】   有N*M的一个非负整数矩阵。现在要把矩阵分成A*B块。矩阵先水平地切A-1刀,把矩阵划分......
  • POJ 3575(计算几何与二分-无尽的小数处理)
    这题写了将近半个月……总是在D各种Bug总的说来-这题最难应该是在精度处理上11001这组数据过了就说明精度处理差不多了……Programkingdom;constmaxn=100;maxm=10......
  • BZOJ 1475(方格取数-递归sap完全+二分图最大点独立集MAXWVIS)
    1475:方格取数TimeLimit: 5Sec  MemoryLimit: 64MBSubmit: 409  Solved: 215[​​Submit​​][​​Status​​][​​Discuss​​]Description......
  • POJ 2398(二分点集)
    DefaultToyStorageDescription在长方形(x1,y1)(x2,y2)中有n块板(保证与上下边相交),和m个点。现给出板和点的位置,求拥有相同点数的区域数、  Inpu......