这个题据说是 OIBH 上面的,但网站大概已经不在了,网上也找不到这道题了,但这是一道好题,我就来写一份题解吧。
问题描述(1s 65536KB)
近日,CWTV 网络电视公司为了提高收视率,举办了一场 CWTV 拳击擂台赛。
一共有 n 名选手参赛,分别为A1,A2……An。拳击赛的举办者对每名参赛选手的实力作了详尽的分析,发现若 Ai 能击败 Aj,则一定有 Ai > Aj。
现在举办者需要制定一个出场次序,第一个出场的作为第一任擂主,然后其他选手依次出场向擂主挑战,凡是挑战者战胜了擂主,那么这个挑战者就顶替原擂主的位置成为新的擂主。
由于举办者希望比赛尽量的精彩,他希望在整个擂台赛中一共更换 k 次擂主。请你帮助他算出满足他的要求的出场次序的个数。
例如:出场顺序 14253 说明了擂主依次是 1,4,5,这符合 n=5 和 k=2。
输入格式
共一行:n,k。n 为参赛人数,k 为更换擂主次数。
规模:0<n<=500,0<=k<n。
输入格式
出场次序的个数。
输入样例1
2 0
输出样例1
1
输入样例2
3 1
输出样例2
3
样例说明
n=2, k=0 有唯一的出场顺序 2 1
n=3, k=1 有出场顺序 1 3 2; 2 3 1; 2 1 3
这题有点难想,打表也许能看出是一类斯特林数。
分析:
更换 k 次擂主,即有 k+1 个擂主,设 m=k+1。
将 n 个人分成 m 组,使每组里有一个擂主,也就是每组里面的最大值,使它排在该组的最前面,
在这一组里,擂主后面的参赛者可以任意排列,因此,每组相当于一个圆排列,这 m 组也就是 m 个圆排列。
这 m 个圆排列间的顺序是无所谓的,为了使后面的擂主替换前面的擂主,不妨使这 m 个圆排列按照它们的擂主大小升序排列。
若新添一个元素,此元素必为 n 个元素中最大的一个,可以使其单独一个圆排列,放到最后;
也可以把它放到前 n-1 个元素任意一个元素的左边,并替换掉这个圆排列里的擂主,然后把该圆排列挪到最后。
也就转换成一类 Stirling 数的圆桌问题了。
而 Stirling 数在 100 以内就会爆 long long,这题 n <= 500,显然要用高精度。
测试数据已上传到链接: https://pan.baidu.com/s/1pM0N6Oj 密码: ai2m。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<string> 5 #include<string.h> 6 #include<vector> 7 using namespace std; 8 9 struct BigInteger{ 10 static const int BASE = 100000000; 11 static const int WIDTH = 8; 12 vector<int> s; 13 14 BigInteger(long long num = 0){ *this = num; } 15 BigInteger operator = (long long num){ 16 s.clear(); 17 do{ 18 s.push_back(num % BASE); 19 num /= BASE; 20 } while (num > 0); 21 return *this; 22 } 23 BigInteger operator = (const string &str){ 24 s.clear(); 25 int x, len = (str.length() - 1) / WIDTH + 1; 26 for (int i=0; i<len; i++) 27 { 28 int end = str.length() - i * WIDTH; 29 int start = max(0, end - WIDTH); 30 sscanf(str.substr(start, end - start).c_str(), "%d", &x); 31 s.push_back(x); 32 } 33 return *this; 34 } 35 BigInteger operator + (const BigInteger &b) const{ 36 BigInteger c, d; 37 c.s.clear(); 38 for (int i = 0, g = 0;; i++) 39 { 40 if (g == 0 && i >= s.size() && i >= b.s.size()) break; 41 int x = g; 42 if (i < s.size()) x += s[i]; 43 if (i < b.s.size()) x += b.s[i]; 44 c.s.push_back(x % BASE); 45 g = x / BASE; 46 } 47 return c; 48 } 49 BigInteger operator * (const long long &b) const{ 50 BigInteger c; 51 c.s.clear(); 52 for(int i = 0,g = 0;; i++) 53 { 54 if(g == 0 && i >= s.size()) break; 55 long long x = 0; 56 if(i < s.size()) x = s[i]*b; 57 x += g; 58 c.s.push_back(x % BASE); 59 g = x / BASE; 60 } 61 return c; 62 } 63 BigInteger operator *= (const long long &b){ 64 *this = *this * b; 65 return *this; 66 } 67 }; 68 ostream &operator<<(ostream &out, const BigInteger &x) 69 { 70 out << x.s.back(); 71 for (int i = x.s.size() - 2; i >= 0; i--) 72 { 73 char buf[20]; 74 sprintf(buf, "%08d", x.s[i]); 75 for (int j = 0; j < strlen(buf); j++) 76 out << buf[j]; 77 } 78 return out; 79 } 80 istream &operator>>(istream &in, BigInteger &x) 81 { 82 string s; 83 if (!(in >> s)) 84 return in; 85 x = s; 86 return in; 87 } 88 89 BigInteger f[2][510]; 90 91 int main() 92 { 93 freopen("c.in","r",stdin); 94 freopen("c.out","w",stdout); 95 96 ios::sync_with_stdio(false); 97 int n,k; 98 cin>>n>>k; 99 BigInteger t=1; 100 f[1][0]=1; 101 for(int i=2;i<=n;++i) 102 { 103 int x=max(0,k-n+i-1),y=min(k,i-2); 104 if(x==0) f[i&1][0]=t,t*=i; 105 f[i&1][i-1]=1; 106 for(int j=x+1;j<=y;++j) 107 f[i&1][j]=f[i&1^1][j-1]+f[i&1^1][j]*(i-1); 108 } 109 cout<<f[n&1][k]; 110 111 return 0; 112 }