暑假集訓 加賽1

HaneDaniko發表於2024-07-22

A.修仙(restart)

注意到這個題顯然可以開一個優先佇列來每次選取最優的解(對結果貢獻最大的,顯然也是值最大的),但是我們會發現能卡掉這個演算法:

4 7
3 4 4 3

顯然是應該選兩邊的.

直接考慮的話太麻煩了,因此我們考慮做一個反悔.

不難發現,對於每一組來說的更優策略只出現在不選它,而選它的左右組合的時候. 因此我們在每次選出一個新的組合的時候,都向優先佇列內插入一個值,用來表示 “取消選擇當前值,而選擇它的左右組合所帶來的價值貢獻”,即 “左方組合貢獻+右方組合貢獻-當前貢獻”,假如這個貢獻比優先佇列中任何一個貢獻都優,那麼我們就採用這個返回方案.

這樣做的話我們就不存在不能選的點了(任何一個點在優先佇列中都有對應的節點,只不過當合並後,兩個點會縮成一個),因此我們可以考慮用雙向連結串列來維護這樣的序列.

賽時初始化的值太小了... 應該直接 memset 的.

這個題細節太多了

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
int n,x,sum;
int a[200001],gx[200001];
struct node{
	int leftid,w;
	bool operator <(const node &A)const{
		if(w==A.w) return leftid<A.leftid;
		return w<A.w; //
	}
};
priority_queue<node>q;
int l[200001],r[200001];
bool hadcon[200001];
void connect(int x){ //x and x+1 make_pair
/*
 -        -     -      -     -
l[l[x]]   l[x]  x-----r[x]  r[r[x]]
*/
	r[l[l[x]]]=x;
	l[r[r[x]]]=x;
	gx[x]=gx[l[x]]+gx[r[x]]-gx[x];
	hadcon[l[x]]=hadcon[r[x]]=1;
	l[x]=l[l[x]];
	r[x]=r[r[x]];
}
void print(priority_queue<node>p){
	while(!p.empty()){
		node u=p.top();p.pop();
		cout<<u.leftid<<" "<<u.w<<endl;
	}
	cout<<endl;
}
signed main(){
//	freopen("restart3.in","r",stdin);
//	freopen("out.out","w",stdout);
	cin>>n>>x;
	for(int i=1;i<=n;++i){
		cin>>a[i];sum+=a[i];
		if(i!=1){
			gx[i-1]=a[i]+a[i-1]-max(a[i]+a[i-1]-x,0ll);
		}
	}
	for(int i=1;i<=n-1;++i){
		l[i]=i-1;
		r[i]=i+1;
//		cout<<" push "<<i<<" "<<gx[i]<<endl;
		q.push({i,gx[i]});
	}
	gx[0]=-inf;gx[n]=-inf;
	l[0]=n;r[n]=0;r[0]=1;l[n]=n-1;
	for(int k=1;k<=n/2;++k){
//		print(q);
		while(q.size() and hadcon[q.top().leftid]){
			q.pop();
		}
		if(q.size()){
//			cout<<"conn "<<q.top().leftid<<endl;
			node u=q.top();q.pop();
			sum-=u.w;
			connect(u.leftid);
			q.push({u.leftid,gx[u.leftid]});
		}
		cout<<sum<<endl;
	}
}
/*
6 10
5 6 3 5 4 5

8 6
3 3 2 5 5 2 3 3 

8 6
3 3 4 3 3 4 3 3
*/ 

B.七負我

考慮兩個沒有邊的點 \((u, v)\) ,設與 \(u\) 相連的點 \(t_i\) 的和為 \(a\) ,設與 \(v\) 相連的點 \(t_i\) 的和為 \(b\) ,那麼這兩個點原先的貢獻為 \(t_u\times a + t_v\times b\) ,容易發現清空貢獻較小的點可以使貢獻變為 \((t_u + t_v)\times \max(a, b)\) 。不斷進行這樣的操作,最終只剩下一個團。

設團的大小為 \(n\) ,則邊數為 \(\tfrac{n\times (n - 1)}{2}\) ,顯然總貢獻為 \((\tfrac{T}{n})^2\times \tfrac{n\times (n - 1)}{2}\) ,簡單化簡發現 \(n\) 越大越好。因此只需要求 最大團 即可.

相關文章