CSP 加賽 1

HaneDaniko發表於2024-09-15

A.小W與夥伴招募

考慮貪心,可以發現,每一天只需要優先選擇價值低的即可

這種貪心思路有一個錯誤的擴充套件,就是先把 \(m\) 天的貨一次性補齊再一次性買,這樣做的問題在於有可能買到次日的貨,而這樣做是不被允許的

考慮放到線段樹上,維護 “節點能夠提供的鑽石數量” 和 “節點花費” 兩個值,只要我們保證價格線上段樹上遞增排列(這是很好做到的,排序再建樹即可),那麼我們就可以透過和平衡樹類似的二分來向下遞迴

  • 如果左子樹滿足當前要求,直接在左子樹裡買
  • 否則,先把左子樹買完,再在右子樹裡買

然後考慮怎麼維護這兩個值

每次補貨只能單點修改,複雜度顯然太高了,因此,我們設計節點數量 \(t=kb_i+s\),其中 \(b_i\)\(i\) 的每日補貨數量,這樣我們在每一天新開始的時候,只需要讓所有的 \(k\) 都增加 \(1\) 就可以了,而這線上段樹上是容易實現的

介於我們並不能同時維護加和乘,所以開兩棵線段樹(當然也可以合成一顆,不過兩者的維護是完全沒有關係的),然後在父結點上維護子節點權值和即可

還有問題就是怎麼在補貨之後比較快地實現 pushdown,可以在外面開兩個字首和來實現(分別對應兩顆線段樹)

然後是 lazytag,針對本題需要開兩個 tag,一個維護補貨時候的 k 增量,一個維護買了東西以後的整體清空,執行的時候先清空再增,注意順序

\(-1\) 直接賦成 \(1e6\) 就行了

另外需要注意的就是,不要直接對 \(m\) 建樹,可以考慮微調一下 sort,讓價值相同的中,數量最多的排在前面,然後你只要遇到一個 \(-1\) 就說明你一直買這個就行了(後面的都更貴),因此直接對此時的 \(i\) 建樹就行了

不過我不明白為啥對 \(m\) 建樹會導致答案錯掉

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
#define int long long
struct tree{
	int l,r;
	int tot_sum,cost_sum;
	int lazy_cover,lazy_k;
}t[200001*4];
int tot_sum[200001],cost_sum[200001];
struct item{
	int a,b;
	bool operator <(const item &A)const{
		if(a==A.a) return b>A.b;
		return a<A.a;
	}
}w[200001];
#ifndef TOOL_H
#define TOOL_H
#endif
template<typename T>
T floor_sqrt(T x,T l=1,T r=-1){
	if(r==-1) r=x;
	int ans=-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(mid*mid<=x){
			l=mid+1;
			ans=mid;
		}
		else{
			r=mid-1;
		}
	}
	return ans;
}
void print(__int128 x,bool first=true){
	if(x<0){
		putchar('-');
		print(-x,false);
		return;
	}
	if(x==0){
		if(first) putchar('0');
		return;
	}
	print(x/10,false);
	putchar((int)(x%10)+'0');
}
template<typename T>
std::string to_string(T x){
	std::string res;bool f=false;
	if(x<0){
		f=true;
		x*=-1;
	}
	while(x){
		res.push_back((int)(x%10)+'0');
		x/=10;
	}
	reverse(res.begin(),res.end());
	if(f) res.push_back('-');
	if(res.empty()) res.push_back('0');
	return res;
}
long long to_number(std::string x){
	long long res=0;bool f=false;
	for(int i=0;i<=(int)x.length()-1;++i){
		if(x[i]=='-'){
			f=true;
		}
		else{
			res=res*10+x[i]-'0';
		}
	}
	return res*(f?-1:1);
}
/*------TOOL_H------*/
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=((l)+(r))/2
void build(int id,int l,int r){
	t[id].l=l;
	t[id].r=r;
	if(l==r) return;
	int mid(l,r);
	build(tol,l,mid);
	build(tor,mid+1,r);
}
void update(int id){
	t[id].tot_sum=t[tol].tot_sum+t[tor].tot_sum;
	t[id].cost_sum=t[tol].cost_sum+t[tor].cost_sum;
}
void pushdown_cover(int id){
	if(t[id].lazy_cover){
		t[tol].tot_sum=t[tol].cost_sum=0;
		t[tor].tot_sum=t[tor].cost_sum=0;
		t[tol].lazy_k=t[tor].lazy_k=0;
		t[tol].lazy_cover=t[tor].lazy_cover=1;
		t[id].lazy_cover=0;
	}
}
void pushdown_k(int id){
	if(t[id].lazy_k){
		t[tol].tot_sum+=t[id].lazy_k*(tot_sum[t[tol].r]-tot_sum[t[tol].l-1]);
		t[tor].tot_sum+=t[id].lazy_k*(tot_sum[t[tor].r]-tot_sum[t[tor].l-1]);
		t[tol].cost_sum+=t[id].lazy_k*(cost_sum[t[tol].r]-cost_sum[t[tol].l-1]);
		t[tor].cost_sum+=t[id].lazy_k*(cost_sum[t[tor].r]-cost_sum[t[tor].l-1]);
		t[tol].lazy_k+=t[id].lazy_k;
		t[tor].lazy_k+=t[id].lazy_k;
		t[id].lazy_k=0;
	}
}
void change(int id,int k){
	if(k>0){
		t[id].tot_sum+=(tot_sum[t[id].r]-tot_sum[t[id].l-1]);
		t[id].cost_sum+=(cost_sum[t[id].r]-cost_sum[t[id].l-1]);
		t[id].lazy_k+=k;
	}
	else{
		t[id].tot_sum=0;
		t[id].cost_sum=0;
		t[id].lazy_k=0;
		t[id].lazy_cover=1;
	}
}
int ask(int id,int k){
	if(t[id].l==t[id].r){
		t[id].tot_sum-=k;
		t[id].cost_sum-=w[t[id].l].a*k;
		return w[t[id].l].a*k;
	}
	pushdown_cover(id);
	pushdown_k(id);
	int res=0;
	if(k<=t[tol].tot_sum){
		res=ask(tol,k);
	}
	else{
		res=t[tol].cost_sum+ask(tor,k-t[tol].tot_sum);
		change(tol,-1);
	}
	update(id);
	return res;
}
int n,m;
int c[200001];
const int inf=1e6;
signed main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	read(n,m);
	for(int i=1;i<=n;++i){
		read(c[i]);
	}
	for(int i=1;i<=m;++i){
		read(w[i].a,w[i].b);
		if(w[i].b==-1) w[i].b=inf;
	}
	sort(w+1,w+m+1);
	for(int i=1;i<=m;++i){
		tot_sum[i]=tot_sum[i-1]+w[i].b;
		cost_sum[i]=cost_sum[i-1]+w[i].a*w[i].b;
		if(w[i].b==inf){
			build(1,1,i);
			break;
		}
	}
	int ans=0;
	for(int i=1;i<=n;++i){
		change(1,1);
		ans+=ask(1,c[i]);
	}
	print(ans);
}

B.小W與制胡串謎題

[](string a,string b){return a+b<b+a;}

相關文章