題目鏈接:
題意:
王國裏有 n 個城堡,初始防禦力爲 1。每個城堡都可以裝備加農炮,裝備之後第 i 個城堡的防禦力爲 w[i]。設現在每個城堡防禦力爲 t[i],王國的防禦力爲 。此外,還有 m 個互斥條件,表示 i,j 不能同時裝備加農炮。設總共有 k 種不同情況,每種情況王國的防禦力爲xi,則求方差 ,其中 。
思路:
先看所求方差:
因此,我們需要知道所有情況數 k,所有情況之和 sum,所有情況的平方和 res。
首先,dfs純暴力做法:每個城堡選或者不選(),再判斷該情況是否滿足互斥條件(),複雜度 。
剪枝:
1. 從第 1 個城堡開始到第 n 個,每次該城堡必選,然後枚舉 i+1 ~ n,把互斥的城堡vis標記都++,再找到後面第一個不互斥的點,2種情況(選或者不選),不選:dfs(i,res,sum,k),選:把其後與其互斥的城堡vis標記都++,dfs(i,res*w[i]*w[i],sum*w[i],k),以此下去,每次把 n 個都遍歷完,更新我們所需要的數值。複雜度 。
2. 若第 i 個點與其後面所有點都不互斥,那麼不需要再考慮該點選或不選,直接dfs(i,res+res*w[i]*w[i],sum+sum*w[i],k*2) 。(即兩種情況組合)複雜度:玄學。
題目數據比較水,這樣就能過。但明顯有反例能卡時間,如:1,2,...,n,且 n 與 2,3,...,n-1 互斥,複雜度: .
第三個優化:
先預處理每個點與其他點互斥的個數,然後按個數從大到小排序,再按上述兩個優化進行操作。這樣能保證與其他點都不互斥的點儘量在後面,且互斥點越多點在越前面。若與其他點都不互斥的點>=20,那麼dfs複雜度 ;若與其他點都不互斥的點<20,那麼剩下的點之間就會有依賴關係,至少也能減一半,dfs複雜度也會 。因此總複雜度 。
這種解法最後跑出來 20 ms,不知道最快的那個老哥 0 ms 是怎麼跑出來的....
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 50;
const ll mod = 1e9 + 7;
typedef struct {
int id, num;
}Point;
int n, m;
ll w[MAX];
Point p[MAX];
int mp[MAX][MAX];
int vis[MAX];
ll res, sum, k;
bool cmp(Point x, Point y) {
return x.num > y.num;
}
//擴展歐幾里得求逆元
ll extendGcd(ll a, ll b, ll &x, ll &y) {
ll ans, t;
if (b == 0) {
x = 1; y = 0;
return a;
}
ans = extendGcd(b, a%b, x, y);
t = x; x = y; y = t - (a / b)*y;
return ans;
}
ll inv(ll a, ll m) {
ll x, y, d;
d = extendGcd(a, m, x, y);
if (d == 1)
return (x%m + m) % m;
else
return -1;
}
void dfs(int root, ll res1, ll sum1, ll k1)
{
if (root > n) {
res = (res + res1) % mod;
sum = (sum + sum1) % mod;
k += k1;
return;
}
for (int i = root + 1; i <= n + 1; i++) {
if (vis[p[i].id]) continue;
if (i == n + 1) {
dfs(i, res1, sum1, k1);
break;
}
int fg = 0;
for (int j = i + 1; j <= n; j++) {
if (mp[p[i].id][p[j].id]) {
fg = 1;
break;
}
}
if (fg == 0) {
dfs(i, (res1 + res1 * w[p[i].id] % mod*w[p[i].id] % mod) % mod, (sum1 + sum1 * w[p[i].id] % mod) % mod, k1 * 2);
break;
}
dfs(i, res1, sum1, k1);
for (int j = i + 1; j <= n; j++) {
if (mp[p[i].id][p[j].id]) {
vis[p[j].id]++;
}
}
dfs(i, res1*w[p[i].id] % mod*w[p[i].id] % mod, sum1*w[p[i].id] % mod, k1);
for (int j = i + 1; j <= n; j++) {
if (mp[p[i].id][p[j].id]) {
vis[p[j].id]--;
}
}
break;
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &w[i]);
w[i] %= mod;
}
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
mp[x][y] = mp[y][x] = 1;
}
res = 1;
sum = 1;
k = 1;
//預處理每個點與其他點互斥的個數,排序
for (int i = 1; i <= n; i++) {
p[i].id = i;
p[i].num = 0;
for (int j = 1; j <= n; j++) {
if (mp[i][j]) {
p[i].num++;
}
}
}
sort(p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
for (int j = i + 1; j <= n; j++) {
if (mp[p[i].id][p[j].id]) {
vis[p[j].id]++;
}
}
dfs(i, w[p[i].id] * w[p[i].id] % mod, w[p[i].id], 1);
}
//公式計算答案
ll invk = inv(k, mod);
ll ans = (res*invk % mod - sum * sum%mod*invk % mod*invk % mod + mod) % mod;
printf("%lld\n", ans);
return 0;
}