1. 程式人生 > >【ZROI8102 D2T2】配對

【ZROI8102 D2T2】配對

題目描述

給定一棵 n 個點的邊有長度的無根樹,小 A 的班裡一共有 m 個男生和 m 個女生,他們各自會等概率出現在樹上 n 個點中的某一個,(注意同一個點上可能會出現多個人)。

然後小 A 會將他們配對成 m 對男女,設 d i s [ i

] 是第 i 對男女在樹上的最短距離,小 A 會選擇使得 i = 1 m
d i s [ i ]
最大的配對方案。

現在小 A 想知道,他選擇的配對方案中

i = 1 m d i s [ i ] 的期望,由於答案可能過大,你只需要輸出答案 n 2 m 後對 1e9+7 取模的值,這個值顯然是一個整數

樣例輸入1

3 1
1 2 1
2 3 1

樣例輸出1

8

樣例輸入2

2 2
1 2 1

樣例輸出2

20

資料範圍

對於 20% 的資料,有 1≤n,m≤4,1≤n,m≤4
對於 50% 的資料,有 1≤n,m≤300,1≤n,m≤300
另有 20% 的資料,滿足最多隻有一條邊的長度 >0
對於 100% 的資料,有 1≤n,m≤2500,1≤n,m≤2500 ,0≤w≤1e4

自述心路歷程+題解

考試時看到這道題時,我的內心是崩潰的。最討厭的就是數學和樹上問題,結果這場考試全都遇到了orz
亂想了兩個多小時,感覺毫無思路,就去拿暴力分。看一眼資料範圍,感覺前20%和特殊的20%是可以拿的,其餘的好像沒思路。。。
敲完暴力水過樣例,懶得檢查,走人。。。
然後這道題我就爆零了啊啊啊啊啊。。。DFS太久沒寫,兩個都寫錯了,原本能得40分的。。。

好的,我們來講一講正解
出題人噸老師表示,可以從特殊資料中找到思路。。。
我們來觀察一下如何分組能使答案最大。如圖。。。
這裡寫圖片描述
我們可以將紫色邊下面的兩點配對,剩下兩點配對,但這顯然不是最優解。我們發現,最優解剛好多了紫色邊權值的兩倍。。。。
於是。。。進行大膽的猜想吧(“證明是留給出題人的。”)。一個圖內,一種解比另一種更優,較劣的解所用到的每一條邊,較優解都是用了的。。簡而言之,對於每一條邊,做一遍處理20%特殊資料的演算法,是沒有問題的(因為貪心原則成立嘛,能走則走)。。
dfs每個點。。。假設某一條邊的兒子房為子樹的節點個數為si,那麼這條邊對答案的貢獻為:
i = 1 m j = 1 m M i n ( i + j , 2 m i j ) ( i m ) ( j m ) s i i + j ( n s i ) 2 m i j v
i和j列舉某一端男女生數量。。。
時間複雜度 O ( n m 2 ) ,要涼。。。
令a=i+j,我們可以發現,對於每個兒子節點,它對各個a的值的影響是可以 O ( n ) 計算的。。。所以我們加一個數組A[],在dfs時儲存貢獻,上邊那個式子改到主函式去計算,將最後3項改為 A [ i + j ] ,就行了。
時間複雜度 O ( n m + m 2 ) ,具體實現見程式碼。。。

程式碼

#include <cstdio>
#include <iostream>
#include <cmath>
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
inline int R()
{
    int p=0;
    char o=GC;
    while(o<48||o>57)o=GC;
    while(o<=57&&o>=48){
        p=(p<<3)+(p<<1)+o-48;
        o=GC;
    }
    return p;
}
const int Q=5005;
int las[Q],e[Q],nn[Q],v[Q],inc=0;
void add(int x,int y,int z)
{
    e[++inc]=y;
    v[inc]=z;
    nn[inc]=las[x];
    las[x]=inc;
}
const int MOD=1e9+7;
inline int add(int a,int b)
{a+=b;return a>=MOD?a-MOD:a;}
inline int ch(int a,int b)
{return 1LL*a*b%MOD;}
int ans=0,n,m,A[5555];
int owo[2555][5555],c[2555][2555];
int dfs1(int x,int fa,int fv)
{
    int si=1;
    for(register int t=las[x];t;t=nn[t]){
        int y=e[t];
        if(y!=fa)si+=dfs1(y,x,v[t]);
    }
    for(int a=0;a<=(m<<1);a++)A[a]=add(A[a],ch(ch(owo[si][a],owo[n-si][2*m-a]),fv));
    return si;
}
void solved()
{
    int i,j;
    for(i=0;i<=n;i++)owo[i][0]=1;
    for(i=1;i<=n;i++)
        for(j=1;j<=(m<<1);j++)
            owo[i][j]=ch(owo[i][j-1],i);
    for(i=0;i<=m;i++){
        c[i][0]=1;
        for(j=1;j<=i;j++)
            c[i][j]=add(c[i-1][j-1],c[i-1][j]);
    }
    dfs1(1,0,0);
    for(register int i=0;i<=m;i++)
        for(register int j=0;j<=m;j++)
            ans=add(ans,ch(ch(min(i+j,2*m-i-j),A[i+j]),ch(c[m][i],c[m][j])));
    printf("%d",ans);
}
int main()
{
    int i,x,y,z;
    n=R(),m=R();
    for(i=1;i<n;i++)
        x=R(),y=R(),z=R(),add(x,y,z),add(y,x,z);
    solved();
    return 0;
}