1005-Little W and Contest
题意:
给定n个点,有两种点,权值分别为1和2,初始时,n个点互不相连。
接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。
要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。
每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。
题解:
要使得三个点权值之和不少于5,有两种选取方案,分别是1、2、2 和 2、2、2.
那么,一开始的方案数sum = C(cnt2,2) * C(cnt1,1) + C(cnt2,3),cnt1表示权值为1的点的个数,cnt2表示权值为2的点的个数。
每次添加新的边,都会减少可行的方案数。利用并查集维护,设u,v为要连接的两个点,pu为u所在联通块的根节点,pv为v所在联通块的根节点,Gu表示u所在联通块,Gv表示v所在联通块,Gt表示剩下的所有点,p1[i]表示以i为根节点的联通块中权值为1的点的个数,p2[i]表示以i为根节点的联通块中权值为2的点的个数,那么,减少的方案数必须是从Gv,Gu,Gt各取一个点(为什么不从Gu取两个点?因为之前已经计算过这种取法,而且不满足三个人互相认识)
分四种情况:
(1) Gu 选取权值为2的点,Gv选取权值为2的点,Gt选取权值为1的点,那么不可行的方案数为
p2[pu] * p2[pv] * (cnt1 - p1[pu] - p1[pv])
(2) Gu 选取权值为2的点,Gv选取权值为2的点,Gt选取权值为2的点,那么不可行的方案数为
p2[pu] * p2[pv] * (cnt2 - p2[pu] - p2[pv])
(3) Gu 选取权值为2的点,Gv选取权值为1的点,Gt选取权值为2的点,那么不可行的方案数为
p2[pu] * p1[pv] * (cnt2 - p2[pu] - p2[pv])
(4) Gu 选取权值为1的点,Gv选取权值为2的点,Gt选取权值为2的点,那么不可行的方案数为
p1[pu] * p2[pv] * (cnt2 - p2[pu] - p2[pv])
AC_CODE:
ll a[maxn],father[maxn];
ll p1[maxn],p2[maxn];
void init(){
for(int i=0; i<=maxn-2; ++i){
father[i] = i;
}
MS0(p1); MS0(p2);
}
int get_father(int x){
if(father[x] == x) return x;
return father[x] = get_father(father[x]);
}
void merge(int x, int y){
x = get_father(x);
y = get_father(y);
if(x != y){
father[x] = y;
p1[y] += p1[x];
p2[y] += p2[x];
p1[x] = 0;
p2[x] = 0;
}
}
void solve(){
int n;
R(n);
init();
int cnt1 = 0, cnt2 = 0;
FOR(i,1,n) {
R(a[i]);
if(a[i] == 1) cnt1 += 1, p1[i] = 1, p2[i] = 0;
else cnt2 += 1, p1[i] = 0, p2[i] = 1;
}
assert((cnt1+cnt2)==n);
ll sum = (qmul(C(cnt2,2),C(cnt1,1)) + C(cnt2,3)) % mod;
W(sum);
FOR(i,2,n) {
int u,v;
R(u,v);
int pu = get_father(u);
int pv = get_father(v);
sum = (sum - (p2[pu] * p2[pv] % mod) * (cnt1 - p1[pu] - p1[pv]) % mod + mod) % mod;
sum = (sum - (p2[pu] * p2[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
sum = (sum - (p2[pu] * p1[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
sum = (sum - (p1[pu] * p2[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
// debug(pu,pv,p1[pu],p1[pv],p2[pu],p2[pv]);
merge(u,v);
// debug(pu,pv,p1[pu],p1[pv],p2[pu],p2[pv]);
W(sum);
assert(p1[v] <= cnt1);
assert(p2[v] <= cnt2);
}
}
Tokitsukaze and Rescue
题意:
给定一个完全图,n个点,n*(n-1)/2条双向边,删除k条边后使1到n的最短路最大,求最大值。
题解:
每一次删边的过程显然是删除最短路中某条边,暴力枚举最短路每条边,然后变成删除(k-1)条边的子问题,重复k次枚举,再计算1到n的最短路,维护最短路最大值即可。
AC_CODE:
const int maxn = 100;
VI G[maxn];
int d[maxn],dis[maxn][maxn],fa[maxn];
int ans,n,k;
void init() {
for(int i=0; i<maxn-1; ++i) G[i].clear();
ans = 0;
}
void dijsktra(int s) {
priority_queue<PII, vector<PII>, greater<PII>> pq;
FOR(i,0,maxn-2) d[i] = 1<<30;
d[s] = 0;
pq.push(MP(0,s));
while(!pq.empty()) {
PII temp = pq.top();
pq.pop();
int v = temp.S;
if(d[v] < temp.F) continue;
for(auto x: G[v]) {
if(d[x] > d[v] + dis[x][v]) {
d[x] = d[v] + dis[x][v];
fa[x] = v;
pq.push(MP(d[x],x));
}
}
}
}
void dfs(int i) {
dijsktra(1);
if(i == k + 1) {
ans = max(ans, d[n]);
return;
}
int new_fa[maxn];
FOR(i,1,n) new_fa[i] = fa[i];
int cur = n;
while(true) {
int x = cur;
int y = new_fa[cur];
int temp = dis[x][y];
dis[x][y] = dis[y][x] = 1<<30;
dfs(i+1);
dis[x][y] = dis[y][x] = temp;
cur = new_fa[cur];
if(cur == 1) break;
}
}
void solve(){
init();
R(n,k);
FOR(i,1,n*(n-1)/2) {
int u,v,w;
R(u,v,w);
G[u].PB(v);
G[v].PB(u);
dis[u][v] = dis[v][u] = w;
}
dfs(1);
W(ans);
}