1. 程式人生 > >Atcoder AGC018 簡要題解

Atcoder AGC018 簡要題解

傳送門

Coins

先轉化為一個點只有兩種屬性 x i , y i x_i,y_i

,選出 X X x i x_i X
+ Y X+Y
x i + y i
x_i+y_i
的最大值。

如果固定選的集合那麼肯定要選 y i y_i k k 大的,我們列舉一下第 k k 大的 y i y_i 是多大然後在兩邊貪心即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=1e5+50;
const LL INF=1e18;
int n,X,Y,Z; 
LL ans=-INF,sum,pre[N],suf[N];
struct atom {LL x,y;}a[N];
priority_queue <LL> q;
int main() {
	X=rd(), Y=rd(), Z=rd(), n=X+Y+Z;
	for(int i=1;i<=n;i++) {
		a[i].x=rd(), a[i].y=rd();
		int c=rd(); sum+=c;
		a[i].x-=c; a[i].y-=c;
		a[i].x-=a[i].y;
	}
	sort(a+1,a+n+1,[](atom c,atom d) {return c.x>d.x;});
	for(int i=1;i<=n;i++) {
		pre[i]=pre[i-1]+a[i].x+a[i].y;
		q.push(-(a[i].x+a[i].y));
		if(q.size()>X) pre[i]+=q.top(), q.pop();
	}
	while(!q.empty()) q.pop();
	for(int i=n;i>=1;i--) {
		suf[i]=suf[i+1]+a[i].y;
		q.push(-a[i].y);
		if(q.size()>Y) suf[i]+=q.top(), q.pop();
	}
	for(int i=X;i+Y<=n;++i) ans=max(ans,pre[i]+suf[i+1]);
	cout<<(ans+sum)<<'\n';
}

Tree and Hamilton Path

算出若每個邊獨立最多被計算多少次。

然後最優情況是這些和減去重心相鄰的某條邊。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=1e5+50;
int n,sze[N],mx_son[N],mx; LL ans;
vector <pii> edge[N];
inline void dfs(int x,int f) {
	sze[x]=1; 
	for(auto v:edge[x])
		if(v.first^f) {
			dfs(v.first,x); sze[x]+=sze[v.first];
			ans+=(LL)v.second*min(sze[v.first],n-sze[v.first])*2;
			mx_son[x]=max(mx_son[x],sze[v.first]);
		}
	mx_son[x]=max(mx_son[x],n-sze[x]);
	mx=min(mx,mx_son[x]);
}
int main() {
	n=rd(); mx=n;
	for(int i=1;i<n;i++) {
		int x=rd(), y=rd(), v=rd();
		edge[x].push_back(pii(y,v));
		edge[y].push_back(pii(x,v));
	} 
	dfs(1,0); 
	int mn=2e9;
	if((!(n&1)) && mx==n/2) {
		for(int i=1;i<=n;i++) if(mx_son[i]==n/2)
			for(auto v:edge[i]) if(mx_son[v.first]==n/2) mn=min(mn,v.second);
	} else {
		for(int i=1;i<=n;i++) if(mx_son[i]==mx)
			for(auto v:edge[i]) mn=min(mn,v.second);
	}
	ans-=mn;
	cout<<ans<<'\n';
}

Sightseeing Plan

挺妙的,一條路徑的貢獻是與中間區域相交的塊的個數。

列舉入口,出口,然後是個二維組合數求和,可以 O ( 1 ) O(1) 計算。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=1e7+50, mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
int X[7],Y[7],ans;
struct binom {
	int fac[N],ifac[N];
	binom() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[N-1]=power(fac[N-1],mod-2);
		for(int i=N-2;~i;i--) ifac[i]=mul(ifac[i+1],i+1);
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
} C;
 
inline int path0(int x,int y) {
	int in=C.C(y-Y[1]+x-X[1]+2,y-Y[1]+1)-C.C(y-Y[1]+x-X[2]+1,y-Y[1]+1);
	in=((LL)in-C.C(y-Y[2