PKUACM 2018 D Chocolate 最小生成树 Kruskal 最长公共子序列

$ \rightarrow $ 戳我进POJ原题

D:Chocolate

总时间限制: 1000ms $ \quad $ 内存限制: 65536kB
 

描述

Vincent is a chocolate enthusiast and a collector of chocolate since he was young.
He has $ N $ chocolates in total.
For simplicity, each chocolate can be denoted by a string (only contains characters "a" - "z").
The difference between two chocolates is defined by the edit distance
with insertion and deletion as the only two edit operations, both at unit cost.
 
For instance, The edit distance between "kitten" and "sitting" is $ 5 $ :

  1. delete k at $ 0 $
  2. insert s at $ 0 $
  3. delete e at $ 4 $
  4. insert i at $ 4 $
  5. insert g at $ 6 $

for a total cost/distance of $ 5 $ operations.
 
Vincent wants to divide his chocolates into some group(s) (specially, one group is also acceptable),
a plan is acceptable if and only if:
The difference between any two chocolates at the same group must be less
than the difference from any chocolate at this group to any chocolate at any other group.
 
Can you help Vincent figure out how many different plans are acceptable?
 

输入

The first line contains an integer $ T (1 \le T \le 30 ) $ , indicating the number of test cases.
 
For each test case:
 
The first line contains one integer $ N ( 2\le N \le 400) $ , indicating the number of chocolates.
Next $ N $ lines, each line contains one string $ S_i $ , each string only contains characters "a" - "z",
and its length won't be larger than $ 20 $ . The total length of strings won't be larger than $ 10,000 $ .
 

输出

For each test case, output your answer in a single line.
An answer should contain one number, indicating the number of different plans.
Since the number can be extremely large, please output the answer modulo $ (10^9 + 7) $ .
 

样例输入

 4
 3
 godivachocolate
 roycechocolate
 richartchocolate
 3
 chocolate
 milkchocolate
 darkchocolate
 4
 ab
 bc
 de
 ef
 6
 ab
 bc
 de
 ef
 gh
 hi

样例输出

 3
 2
 5 
 9

 

提示

In third case, there are $ 5 $ acceptable plans:

  1. (ab)(bc)(de)(ef)
  2. (ab)(bc)(de, ef)
  3. (ab, bc)(de)(ef)
  4. (ab, bc)(de, ef)
  5. (ab, bc, de, ef)

In fourth case, there are $ (2^3+1) $ acceptable plans.
 

来源

PKU Campus 2018
 

题目大意

  • 给定一张无向完全图,把它分成几个连通块,使得:

  • 每个连通块内部点之间的距离 $ < $ 该连通块的与其他连通块的点的距离

  • 求划分方案数

  • $ N \le 400 $

  • 注:无向图是通过若干字符串及其编辑距离产生的
     

题解

  • 对无向图模拟一遍 $ Kruskal $ ,设 $ F(S) $ 表示集合 $ S $ 划分满足要求的连通块的方案数

  • 合并两个集合 $ S_x,S_y $ 时:

  • 若分别属于 $ S_x, S_y $ 的点要分在同一连通块,则 $ S_x \cup S_y $ 只能整个构成连通块

  • 若 $ S_x \cup S_y $ 满足题目要求,则 $ F( S_x \cup S_y )=F(S_x) \times F(S_y) +1 $

  • 否则 $ F( S_x \cup S_y )=F(S_x) \times F(S_y) $
     

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define mod 1000000007
#define int long long
#define maxm 200000
struct edge{ int u,v,w; }e[maxm];
int t,n,dis[500][500],c[25][25],f[500],cnt,ans,tot,F[500],pot[500][500];
string s[500];
bool cmp(edge x,edge y){ return x.w<y.w; }
int LCS(string x,string y){
    int xlen=x.size(),ylen=y.size();
    for(int i=1;i<=xlen;++i) c[i][0]=0;
    for(int i=1;i<=ylen;++i) c[0][i]=0;
    for(int i=1;i<=xlen;++i)
        for(int j=1;j<=ylen;++j)
            if(x[i-1]==y[j-1]) c[i][j]=c[i-1][j-1]+1;
            else c[i][j]=max(c[i][j-1],c[i-1][j]);
    return c[xlen][ylen];       
}
int find(int x){
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
bool check(int u){
    int minn=mod,maxn=-mod;
    for(int fu,fv,i=1;i<=cnt;++i){
        fu=find(e[i].u); fv=find(e[i].v);
        if(fu==u&&fv==u) maxn=max(maxn,e[i].w);
        else if((fv==u&&fu!=u)||(fv!=u&&fu==u)) 
            minn=min(minn,e[i].w);
    }
    return maxn<minn;
}
signed main(){
    scanf("%lld",&t);
    while(t--){
        scanf("%lld",&n); 
        cnt=tot=0;
        for(int i=1;i<=n;++i){ cin>>s[i]; f[i]=i; F[i]=1; }
        for(int i=1;i<=n;++i)
            for(int j=i+1;j<=n;++j){
                e[++cnt].u=i; e[cnt].v=j;
                e[cnt].w=dis[i][j]=dis[j][i]=s[i].size()+s[j].size()-2*LCS(s[i],s[j]);
            }
        sort(e+1,e+1+cnt,cmp);
        for(int fu,fv,i=1;i<=cnt;++i){
            fu=find(e[i].u); fv=find(e[i].v);
            if(fu==fv) continue;
            f[fu]=fv;
            F[fv]=F[fu]*F[fv]%mod;
            if(check(fv)) F[fv]=(F[fv]+1)%mod;
            ++tot; if(tot==n-1){ ans=F[fv]; break; }
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PotremZ/p/9610204.html