1. 程式人生 > 其它 >題解 小P的生成樹

題解 小P的生成樹

傳送門

完全沒有思路

  • 對複數的和求最值:假設已知最值複數的單位向量,那要構成這個最值向量就要使各複數在該方向向量上的投影之和最大
    對於兩個複數,可以求出它們在方向向量上的投影相等時的方向向量
    列舉複數可以得到許多方向向量,這些向量會將單位圓劃分成許多區間,這些區間中各複數的投影大小關係不變
    於是可以在每個區間中隨便選一個單位向量,取每個向量的投影長正常求最值,最後對每個區間的結果求最值即可
  • 對於生成樹:生成樹的形態只與各邊邊權的相對大小有關,而與具體權值無關(即只需要能將邊排序)

對於這題,對每個區間跑個最大生成樹就好

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define reg register int
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
struct edge{int from, to, a, b; double val;}e[N];

namespace force{
	bool vis[15];
	void solve() {
		int lim=1<<m;
		double ans=0;
		for (int s=1,s2,cnt; s<lim; ++s) {
			s2=s; cnt=0;
			ll suma=0, sumb=0;
			double sum=0;
			do {++cnt; s2&=s2-1;} while (s2);
			if (cnt!=n-1) goto jump;
			for (int i=1; i<=n; ++i) vis[i]=0;
			for (int i=0; i<m; ++i) if (s&(1<<i)) {
				vis[e[i+1].from]=vis[e[i+1].to]=1;
				suma+=e[i+1].a, sumb+=e[i+1].b;
			}
			sum=sqrt(suma*suma+sumb*sumb);
			if (sum<ans) goto jump;
			for (int i=1; i<=n; ++i) if (!vis[i]) goto jump;
			// cout<<"sum: "<<sum<<' '<<bitset<10>(s)<<endl;
			ans=sum;
			jump: ;
		}
		printf("%.6lf\n", ans);
		exit(0);
	}
}

namespace task1{
	int fa[N], ans;
	inline int find(int p) {return fa[p]==p?p:fa[p]=find(fa[p]);}
	void solve() {
		int sum=0;
		for (int i=1; i<=n; ++i) fa[i]=i;
		sort(e+1, e+m+1, [](edge a, edge b){return a.a>b.a;});
		int tot=0;
		for (int i=1; i<=m&&tot<n-1; ++i) {
			int f1=find(e[i].from), f2=find(e[i].to);
			if (f1!=f2) {
				fa[f1]=f2;
				sum+=e[i].a;
				++tot;
			}
		}
		ans=abs(sum);
		sum=0;
		for (int i=1; i<=n; ++i) fa[i]=i;
		sort(e+1, e+m+1, [](edge a, edge b){return a.a<b.a;});
		tot=0;
		for (int i=1; i<=m&&tot<n-1; ++i) {
			int f1=find(e[i].from), f2=find(e[i].to);
			if (f1!=f2) {
				fa[f1]=f2;
				sum+=e[i].a;
				++tot;
			}
		}
		ans=max(ans, abs(sum));
		double tem=ans;
		printf("%.6lf\n", tem);
		exit(0);
	}
}

namespace task{
	int fa[N], top;
	const double pi=acos(-1.0);
	double alpha[N];
	inline double calc(double x1, double x2, double y1, double y2) {return (x1-x2)/(y2-y1);}
	inline int find(int p) {return fa[p]==p?p:fa[p]=find(fa[p]);}
	double kruskal(double base) {
		for (int i=1; i<=n; ++i) fa[i]=i;
		for (int i=1; i<=m; ++i) e[i].val=e[i].a*cos(base)+e[i].b*sin(base);
		sort(e+1, e+m+1, [](edge a, edge b){return a.val>b.val;});
		int tot=0; ll suma=0, sumb=0;
		for (int i=1; i<=m&&tot<n-1; ++i) {
			int f1=find(e[i].from), f2=find(e[i].to);
			if (f1!=f2) {
				fa[f1]=f2;
				suma+=e[i].a, sumb+=e[i].b;
				++tot;
			}
		}
		// cout<<"sum: "<<suma<<' '<<sumb<<endl;
		return sqrt(suma*suma+sumb*sumb);
	}
	void solve() {
		alpha[++top]=90; alpha[++top]=270;
		for (int i=1; i<=m; ++i)
			for (int j=i+1; j<=m; ++j) if (e[i].a!=e[j].a && e[i].b!=e[j].b) {
				double t=atan2(e[i].a-e[j].a, e[i].b-e[j].b);
				// cout<<"t: "<<t<<endl;
				alpha[++top]=t; alpha[++top]=(t+pi);
			}
		sort(alpha+1, alpha+top+1);
		double ans=0;
		for (int i=2; i<=top; ++i) ans=max(ans, kruskal((alpha[i-1]+alpha[i])/2));
		ans=max(ans, kruskal(0));
		printf("%.6lf\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("mst.in", "r", stdin);
	freopen("mst.out", "w", stdout);

	n=read(); m=read();
	bool itgt=1;
	for (int i=1,u,v,a,b; i<=m; ++i) {
		u=read(); v=read(); a=read(); b=read();
		e[i].from=u; e[i].to=v; e[i].a=a; e[i].b=b;
		if (b) itgt=0;
	}
	// if (itgt) task1::solve();
	// else force::solve();
	task::solve();

	return 0;
}