1. 程式人生 > 實用技巧 >CodeForces 1361D Johnny and James

CodeForces 1361D Johnny and James

CodeForces 1361D Johnny and James

https://codeforces.com/contest/1361/problem/D

Tutorial

https://codeforces.com/blog/entry/78355?f0a28=1

可以把這道題看作這樣一個結構,以\((0,0)\)為根的樹,且\((0,0)\)的每個子樹都是一條鏈.

考慮一個基地如果保留的話它對答案的貢獻.設它所在鏈上距離比它大的被選擇的點數為\(t\),它到原點的距離為\(d\),那麼貢獻為\(d((k-t-1)-t)=d(k-2t-1)\).

從這個式子中可以看出,貢獻係數為正的時候\(d\)儘量大,否則\(d\)

儘量小,所以每條鏈上所選擇的是一個字首和一個字尾.

類比重心,發現當\(k-2t-1 \ge 0\)也就是一條鏈上選擇的節點數不超過\(\lfloor \dfrac k2 \rfloor\)時,貢獻係數是非負的.所以只有當\(n-mx < k-\lfloor \dfrac k2 \rfloor\) 其中\(mx\)為最大的鏈的長度,時,才會有一條鏈選擇一段字首.也就是說長度最長的鏈之外的節點全部保留,最長鏈上保留最後\(\lfloor \dfrac k2 \rfloor\)個點和最前面的\(k-(n-mx)-\lfloor \dfrac k2 \rfloor\)個點.

否則,由於每次若選擇一條鏈中的節點,那麼一定是選擇這條鏈中最遠的節點,那麼可以對於貢獻維護優先佇列,每次貪心選擇貢獻最大的節點即可.

複雜度\(O(n \log n)\)

Code

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char gc() {
	return getchar();
	static char buf[100000],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
	x=0; int f=1,ch=gc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
	x*=f;
}
template<class T> inline bool Cmax(T &x,T y) {return x<y?x=y,1:0;}
typedef long long ll;
const int maxn=5e5+50;
int n,k,mx;
int ncnt;
map< pair<int,int>, int> id;
vector<double> rec[maxn];
int gcd(int a,int b) {return b==0?a:gcd(b,a%b);}
struct node {
	int x,y; double d;
	node(int x=0,int y=0,double d=0):x(x),y(y),d(d){}
	void init() {
		d=sqrt((ll)x*x+(ll)y*y);
		if(x==0&&y==0) return;
		int t=abs(gcd(x,y));
		x/=t,y/=t;
	}
	inline bool operator <(const node &other) const {
		return d<other.d;
	}
} a[maxn];
void sol0() {
//	debug("0---\n");
	double an=0;
	for(int i=1;i<=ncnt;++i) {
		if(rec[i].size()!=mx) {
			for(int j=0;j<rec[i].size();++j) {
				int t=rec[i].size()-j-1;
//				debug("%d %d\n",j,t);
				an+=rec[i][j]*(k-t*2-1);
			}
		}
		else {
			for(int t=0;t<k/2;++t) {
				int j=rec[i].size()-t-1;
//				debug("%d %d\n",j,t);
				an+=rec[i][j]*(k-t*2-1);
			}
			int lim=(k+1)/2-(n-mx);
			for(int j=0;j<lim;++j) {
				int t=k/2+lim-j-1;
//				debug("%d %d\n",j,t);
				an+=rec[i][j]*(k-t*2-1);
			}
		}
	}
	printf("%.12lf\n",an);
}
void sol1() {
//	debug("1---\n");
	double an=0;
	priority_queue<node> q;
	q.push(node(0,0,0));
	for(int i=1;i<=ncnt;++i) q.push(node(i,rec[i].size()-1,rec[i].back()*(k-1)));
	for(int i=1;i<=k;++i) {
		int x=q.top().x,y=q.top().y; an+=q.top().d; q.pop(); 
		if(y==0) continue;
		int t=rec[x].size()-y;
		q.push(node(x,y-1,rec[x][y-1]*(k-t*2-1)));
	}
	printf("%.12lf\n",an);
}
int main() {
	rd(n),rd(k);
	for(int i=1;i<=n;++i) rd(a[i].x),rd(a[i].y),a[i].init();
	sort(a+1,a+n+1);
	for(int i=2;i<=n;++i) {
		pair<int,int> t=make_pair(a[i].x,a[i].y);
		if(!id[t]) id[t]=++ncnt;
		rec[id[t]].push_back(a[i].d);
	}
	for(int i=1;i<=ncnt;++i) Cmax(mx,(int)rec[i].size());
	if(n-mx<(k+1)/2) sol0();
	else sol1();
	return 0;
}