FJ省队集训最终测试 T2

思路:发现如果一个人一共选了x个点,那么选中某一个点对的概率都是一样的,一个人选x个点的总方案是C(n,x),一个人选中某个点对的总方案是C(n-2,x-2),这样,那么选中某个点对的概率就是 x*(x-1)/(n*(n-1)),这样,我们就用树分治求出有多少对符合条件的对数,然后乘上每个人的概率即可。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define inf 0x7fffffff
 7 int son[200005],F[200005],root,vis[200005],pd[200005],c[200005];
 8 int n,go[200005],tot,first[200005],next[200005],dis[200005],num,sz,m,b[200005];
 9 int ans,cnt[200005];
10 double Ans;
11 int read(){
12     int t=0,f=1;char ch=getchar();
13     while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f=-1;ch=getchar();}
14     while (‘0‘<=ch&&ch<=‘9‘){t=t*10+ch-‘0‘;ch=getchar();}
15     return t*f;
16 }
17 void insert(int x,int y){
18     tot++;
19     go[tot]=y;
20     next[tot]=first[x];
21     first[x]=tot;
22 }
23 void add(int x,int y){
24     insert(x,y);insert(y,x);
25 }
26 void findroot(int x,int fa){
27     son[x]=1;F[x]=0;
28     for (int i=first[x];i;i=next[i]){
29         int pur=go[i];
30         if (vis[pur]||pur==fa) continue;
31         findroot(pur,x);
32         son[x]+=son[pur];
33         F[x]=std::max(F[x],son[pur]);
34     }
35     F[x]=std::max(F[x],num-son[x]);
36     if (F[x]<F[root]) root=x;
37 }
38 void bfs(int x){
39     int h=1,t=1;c[h]=x;pd[x]=sz;dis[x]=1;
40     while (h<=t){
41         int now=c[h++];
42         for (int i=first[now];i;i=next[i]){
43           int pur=go[i];
44           if (vis[pur]||pd[pur]) continue;
45           pd[pur]=sz;
46           dis[pur]=dis[now]+1;
47           c[++t]=pur;
48         }
49     }
50     for (int j=1;j<=m;j++)
51      for (int i=1;i<=t;i++)
52       if (b[j]>=dis[c[i]])
53       ans+=cnt[b[j]-dis[c[i]]];
54     for (int i=1;i<=t;i++)
55      cnt[dis[c[i]]]++;
56 }
57 void solve(int x,int fa){
58     vis[x]=1;sz++;
59     memset(cnt,0,sizeof cnt);cnt[0]=1;
60     for (int i=first[x];i;i=next[i]){
61         int pur=go[i];
62         if (vis[pur]) continue;
63         bfs(pur);
64     }
65     int Sum=num;
66     for (int i=first[x];i;i=next[i]){
67         int pur=go[i];
68         if (vis[pur]) continue;
69         if (son[pur]>son[x]) num=Sum-son[x];
70         else num=son[pur];
71         root=0;
72         findroot(pur,0);
73         solve(root,x);
74     }
75 }
76 int main(){
77     n=read();m=read();
78     for (int i=1;i<=m;i++){
79         b[i]=read();
80     }
81     std::sort(b+1,b+1+m);
82     for (int i=1;i<n;i++){
83         int u=read(),v=read();
84         add(u,v);
85     }
86     F[0]=inf;root=0;num=n;
87     findroot(1,0);
88     solve(root,0);
89     double Ans=(((double)ans)/((double)n))/((double)n-1);
90     int m=n/3;
91     if (n%3) printf("%.2lf\n",Ans*(m+1)*(m));
92     else printf("%.2lf\n",Ans*(m-1)*m);
93     if (n%3==2) printf("%.2lf\n",Ans*(m+1)*(m));
94     else printf("%.2lf\n",Ans*(m-1)*m);
95     printf("%.2lf\n",Ans*(m-1)*m);
96 }
时间: 2024-08-05 19:13:10

FJ省队集训最终测试 T2的相关文章

FJ省队集训最终测试 T3

思路:状态压缩dp,f[i][j[[k]代表i行j列这个格子,连续的状态为k,这个连续的状态是什么?就是下图 X格子代表我当前走到的地方,而这里的状态就是红色部分,也就是连续的一段n的状态,我们是分每一位计算的,这样就可以转移了,注意,当当前点在最下面的时候要额外计算一个与1的贡献. 坑爹,inf设小了只有30分. 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstrin

FJ省队集训DAY2 T2

思路:我们可以考虑三角剖分,这样问题就变成考虑三角形的选取概率和三角形内有多少个点了. 先用树状数组预处理出三角剖分的三角形中有多少个点,然后用线段树维护,先用原点极角排序,然后枚举i,再以i极角排序,此时线段树的作用就来了,每次到一个询问的教室点,我们就在线段树里面查找之前的概率,统计贡献即可. 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #

FJ省队集训DAY4 T2

XXX 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 using namespace std; 8 typedef unsigned long long ll; 9 ll a,mod=1,L=1; 10 ll tr[4],b[4],tmp[4

FJ省队集训DAY3 T2

思路:如果一个DAG要的路径上只要一条边去切掉,那么要怎么求?很容易就想到最小割,但是如果直接做最小割会走出重复的部分,那我们就这样:反向边设为inf,这样最小割的时候就不会割到了,判断无解我们直接用tarjan 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define ll long

FJ省队集训DAY1 T1

题意:有一堆兔子,还有一个r为半径的圆,要求找到最大集合满足这个集合里的兔子两两连边的直线不经过圆. 思路:发现如果有两个点之间连边不经过圆,那么他们到圆的切线会构成一段区间,那么这两个点的区间一定会有交集,形如s0 s1 e0 e1 同样的,如果是n个点,那就是s0 s1 s2..sn e0 e1 e2.. en 因此,我们枚举那个起始点,然后对于其他点我们按照s排序,对于e做最长上升子序列即可.时间复杂度O(n^2 logn) 1 #include <cstdio> 2 #include

FJ省队集训DAY3 T1

思路:我们考虑如果取掉一个部分,那么能影响到最优解的只有离它最近的那两个部分. 因此我们考虑堆维护最小的部分,离散化离散掉区间,然后用线段树维护区间有没有雪,最后用平衡树在线段的左右端点上面维护最小的id 我讲的貌似不是很清楚.. 还有,蜜汁80分,打死也改不出来.. 1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algo

FJ省队集训DAY2 T1

思路:转换成n条三维空间的直线,求最大的集合使得两两有交点. 有两种情况:第一种是以某2条直线为平面,这时候只要统计这个平面上有几条斜率不同的直线就可以了 还有一种是全部交于同一点,这个也只要判断就可以了. 然后我并不能改出来,wa了好多个点 WA的程序: 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm&

FJ省队集训DAY5 T1

思路:考试的时候打了LCT,自以为能过,没想到只能过80.. 考完一想:lct的做法点数是100W,就算是nlogn也会T. 讲一下lct的做法把:首先如果一条边连接的两个点都在同一个联通块内,那么这条边对答案没有影响,可以忽略,因此,问题变成了每次询问两个点中路径上权值最大的边(这里的权值我们令它为加入这条边的时间),边我们用一个点连接两个端点来表示. 正解:由于是无根树,因此我们用并查集按秩合并,每次把小的加到大的里面去,询问的时候暴力走lct查找最大即可. 1 #include<cstdi

FJ省队集训DAY4 T1

直接上题解 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define ll long long 7 const int Mod=1000000009,N=3000; 8 ll jc[N+10],jcny[N+10],jcnys[N+10],K[N+10],p[N+10],f[N+10];