題目鏈接:
https://www.hackerrank.com/challenges/connect-the-country
由於n的整數拆分不超過6000 我們可以考慮用每個整數拆分表示一個狀態
預處理每個狀態之間的轉移 然後暴力跑
複雜度不是很會證明 反正跑得過去
#include<cstdio>
#include<map>
#include<cmath>
#include<cstring>
#include<vector>
#include<cstdlib>
using namespace std;
char c;
inline void read(int&a)
{a=0;do c=getchar();while(c<'0'||c>'9');while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();}
int cnt;
struct RE
{
int con[31];
inline int &operator[](int x){return con[x];}
inline friend bool operator < (RE a,RE b)
{
for(int i=0;i<=a[0];i++)
if(a[i]^b[i])
return a[i]<b[i];
return false;
}
}A[6001];
int cac[31];
int n;
map<RE,int>M;
map<int,int>R;
void Solve(int x,int y,int z)
{
if(!x)
{
A[++cnt][0]=n;
for(int i=1;i<=z;i++)
A[cnt][cac[i]]++;
M[A[cnt]]=cnt;
return;
}
for(int i=y;i<=x;i++)
cac[z+1]=i,Solve(x-i,i,z+1);
}
pair<int,int> Trans[6000][600];
int s[6000];
double P[2][6001];
const
double eps=1e-8;
int main()
{
double D=sqrt(2.0),T=sqrt(3.0);
double A76=sin(D)-sin(T);
// printf("%.10f\n%.10f\n",1.0,4*sqrt(5));
read(n);
//n=4;
Solve(n,1,0);
for(int i=1;i<=cnt;i++)
{
R.clear();
RE P=A[i];
int Sum=n*(n-1)>>1;
for(int j=1;j<A[i][0];j++)
if(A[i][j])
{
for(int k=j+1;k<=A[i][0];k++)
if(A[i][k])
{
P[j]--,P[k]--,P[j+k]++;
int no=M[P];
P[j]++,P[k]++,P[j+k]--;
int pp=j*k*P[j]*P[k];
R[no]+=pp;
Sum-=pp;
}
if(P[j]>1)
{
int k=j;
P[j]--,P[k]--,P[j+k]++;
int no=M[P];
P[j]++,P[k]++,P[j+k]--;
int pp=j*k*P[j]*(P[k]-1)>>1;
R[no]+=pp;
Sum-=pp;
}
}
//R[i]=Sum;
for(map<int,int>::iterator A=R.begin();A!=R.end();A++)
Trans[i][++s[i]]=make_pair(A->first,A->second);
}
int Sum=n*(n-1)>>1;
int last=1,now=0;
double ans=0;
int S;
P[last][1]=1;
for(int i=1;i<=Sum;i++,last^=1,now^=1)
{
double F=1./(Sum-i+1);
S=Sum-i+1;
memset(P[now],0,sizeof(P[now]));
for(int j=1;j<cnt;j++)
{
if(P[last][j]>eps)
{
for(int k=1;k<=s[j];k++)
{
P[now][Trans[j][k].first]+=P[last][j]*Trans[j][k].second*F;
S-=Trans[j][k].second;
}
P[now][j]+=S*F*P[last][j];
S=Sum-i+1;
}
}
ans+=P[now][cnt]*i;
}
printf("%d\n",(int)(ans+eps));
return 0;
}