【题解】QOJ 7221. The Road Network
本题被以下比赛引用:
- ICPCCamp 2016. Day 4. SJTU Dreadnought Contest
- Moscow International Workshops 2016. Day 3. SJTU Dreadnought Contest
题解:
我们可以把点分成三个集合:\(A,B,C\),其中 \(A\) 是一个团,\(B\) 中每个点和 \(A\) 中 \(1\) 到 \(|A|-1\) 个点有边(内部没有边),\(C\) 中是孤立点。
枚举一个集合中 \(A\) 集合的点数为 \(i\),那么答案一定可以取到 \(i(|A|-i)+\sum_{x\in B} \min\{deg(x),\max\{i,|A|-i\}\}\)。(\(A\) 类点之间的连边 \(+\) \(B\) 类点和 \(A\) 类点的连边)
构造方式即为:一个集合中放前 \(i\) 大的 \(A\) 类点,另一个集合放剩下的 \(A\) 类点和所有的 \(B\) 类点。
接下来考虑计数:
\(C\) 中每个点对方案数的贡献显然是 \(2\),所以说 \(C\) 类点对答案的贡献是 \(2^{|C|}\)
我们现在的目标是保证一个集合取到了能达到答案的 \(A\) 集合的点的个数,然后挪动 \(B\) 类点所在的集合使得答案不变,也就是说需要保证 \(\sum_{x\in B} \min\{deg(x),\max\{i,|A|-i\}\}\) 不发生变化。
在 \(A\) 集合 \(w\) 最大的点和最小的点分别在集合 \(X,Y\) 的情况下,我们考虑一个集合中 \(B\) 类点 \(x \in Y\) 对于另一个集合中 \(A\) 类点的限制:
- 若 \(deg(x) > \max\{i,|A|-i\}\) ,集合 \(X\) 只能有 \(w\) 前 \(deg(x)\) 大的点
- 若 \(deg(x) \leq \max\{i,|A|-i\}\) ,集合 \(X\) 必须有 \(w\) 前 \(deg(x)\) 大的所有点
方案数就是一个组合数。
在 \(A\) 集合 \(w\) 最大的点和最小的点在同一集合 \(X\) 的情况下,我们考虑 \(B\) 类点 \(x\) 对于另一个集合中 \(A\) 类点的限制:
- 若 \(deg(x) > \max\{i,|A|-i\}\) ,\(x\) 放入集合 \(X\),集合 \(Y\) 中只能有 \(w\) 前 \(deg(x)\) 大的点
- 若 \(deg(x) \leq \max\{i,|A|-i\}\) ,\(x\) 放入集合 \(Y\),集合 \(X\) 必须有 \(w\) 前 \(deg(x)\) 大的所有点
方案数也是一个组合数
最后还有一些细节:
- 当 \(i == |A|-i\) 时,因为可以交换两个集合所以需要算两遍
- 当 \(B\) 中的点的 \(deg\) 非常小时,没有必须放在 \(w\) 最大的点所在集合的另一个集合的限制,所以也可以对 \(B\) 集合放的方案进行镜像,也要算两遍。
时间复杂度 \(O(n^2)\)。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7,NN = 2e3 + 8;
ll n,d;
ll A,B,C;
ll w[NN],deg[NN];
ll fac[NN],inv[NN];
ll ans,f[NN],cnt;
inline ll read(){
register ll res = 0;
register char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) res = res * 10 + c - 48,c = getchar();
return res;
}
ll ksm(ll x,ll k){
ll res = 1;
while(k){
if(k & 1) res = res * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return res;
}
ll binom(int n,int m){
if(n < m) return 0;
return fac[n] * inv[m] % MOD * inv[n-m] % MOD;
}
void init(){
fac[0] = 1;
for(int i = 1; i <= n; ++i) fac[i] = fac[i-1] * i % MOD;
inv[n] = ksm(fac[n],MOD - 2);
for(int i = n; i >= 1; --i) inv[i-1] = inv[i] * i % MOD;
}
int main(){
n = read(); d = read(); init();
for(ll i = 1; i <= n; ++i) w[i] = read();
sort(w+1,w+1+n,greater<ll>());
A = 1;
while(A < n && w[A] + w[A+1] >= d) ++A;
B = A;
while(B < n && w[B+1] + w[1] >= d) ++B;
if(A == 1) return printf("0 %lld\n",ksm(2,n)),0;
//deg 1~A 是 A 类点,deg A+1~B 是 B 类点,deg B+1~n 是 C 类点
for(ll i = 1; i <= n; ++i){
for(ll j = 1; j <= n; ++j){
if(i == j) continue;
deg[i] += (w[i] + w[j] >= d);
}
}
for(ll i = 0; i <= A; ++i){
ll res = i * (A - i);
for(ll j = A+1; j <= B; ++j){
res += min(deg[j], max(i, A - i));
}
f[i] = res;
ans = max(ans,res);
}//Calc ans
for(ll i = 0; i <= A; ++i) if(ans == f[i]){
ll l = 0, r = A;
for(ll j = A+1; j <= B; ++j){
if(deg[j] > max(i, A-i)) r = min(r,deg[j]);
else l = max(l,deg[j]);
}
cnt = (cnt + binom(r-l, max(i,A-i) - l)) % MOD;
if(A < B && (deg[A+1] <= min(i, A-i) || i == A-i)) cnt = (cnt+binom(r-l, min(i, A-i)-l)) % MOD;
if(A < B && deg[A+1] > max(i, A-i) && deg[B] < max(i, A-i)){
l = 0, r = min(i, A-i);
for(int j = A+1; j <= B; ++j)
if(deg[j] > max(i, A-i)) r = min(r, deg[j] - max(i, A-i));
else l = max(l, deg[j]);
if(l <= r) cnt = (cnt + binom(r-l+max(i, A-i), r-l) * (1+(i == A-i))) % MOD;
}
}
cnt = cnt * ksm(2,n-B) % MOD;
printf("%lld %lld",ans,cnt);
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/17950733/solution_QOJ7221

浙公网安备 33010602011771号