神特麼圖論題ORZORZORZ,fong了
大概就是通過一種操作得到一個圖,求這個圖的最大匹配,和取得最大的可能種數
思路倒是很容易看懂,這個代碼也太emmm了吧
做法:
1.按照操作逆着dp,還原回最後只有一條邊時的情況
2.注意處理每次更新的步驟(剛開始完全沒想到還有可能已經存在一條相同的邊,更新也一開始想錯了
3.好多STL的應用,學到遼學到遼,STL真實一竅不通啊
4.補了題我在比賽的時候也寫不出這種東西吧……菜雞咆哮
#include<iostream>
#include<string.h>
#include<assert.h>
#include<set>
#include<map>
#include<queue>
#define go(i,a,b) for (ll i=a;i<=b;i++)
#define ll long long
#define N 100005
#define MOD 1000000007
using namespace std;
typedef pair<ll,ll>pii;
struct edge{
ll u,v;
ll dp[2][2],sum[2][2];
bool operator<(const edge &e) const {
return v<e.v;
}
//set中的元素如果是結構體必須重載該運算符
//因爲set中的元素是按照大小排列的,erase比較的也是這個位置
//這裏的const如果不加的話運算符重載即失敗
};
void updata(ll &w, ll &sum, ll w1, ll sum1){
if (w1==-1) return;
if (w1>w) w=w1,sum=sum1;
else if (w1==w) sum=(sum+sum1)%MOD;
//更新數據
}
set<edge> e[N];
int main(){
ll T,u,v,w,ca,n,m;
map<pii,pii>::iterator it;
set<edge>::iterator sit;
scanf("%lld",&T);
for (ll ca=1;ca<=T;ca++){
scanf("%lld%lld",&n,&m);
map<pii,pii>mp;//讀入存在map中,重複邊僅保留最優,並保存最優邊的個數
go(i,1,m){
scanf("%lld%lld%lld",&u,&v,&w);
if (u>v) swap(u,v);
pii p=make_pair(u,v);
if (!mp.count(p)) mp[p]=make_pair(w,0);
it=mp.find(p);
if (it->second.first<w) it->second=make_pair(w,1);
else if (it->second.first==w) it->second.second++;
}
go(i,1,n) e[i].clear();//把map中的邊都存到set中,好讀取
for (it=mp.begin();it!=mp.end();it++){
edge ne;
ne.u=it->first.first;
ne.v=it->first.second;
memset(ne.dp,-1,sizeof(ne.dp));
memset(ne.sum,-1,sizeof(ne.sum));
ne.dp[0][0]=0;
ne.sum[0][0]=1;
ne.dp[1][1]=it->second.first;
ne.sum[1][1]=it->second.second;
e[ne.u].insert(ne);
swap(ne.u,ne.v);
e[ne.u].insert(ne);
}
if (n==2){//只有兩個點直接輸出
it=mp.begin();
printf("%d %d\n",it->second.first,it->second.second);
continue;
}
queue<ll>q;
go(i,1,n){//將度爲2的點進隊列
if (e[i].size()==2) q.push(i);
}
go(i,1,n-2){
ll u=q.front();
q.pop();
edge a=*e[u].begin(); //a.v->a.u==b.u->b.v
e[u].erase(e[u].begin());
edge b=*e[u].begin();
e[u].erase(e[u].begin());//刪去這個點的兩個出邊
edge ne;
ne.v=u;
e[a.v].erase(ne);//刪除到達當前點的入邊
e[b.v].erase(ne);
ne.u=a.v;
ne.v=b.v;
ll dp[2][2],sum[2][2];
memset(dp,-1,sizeof(dp));memset(sum,-1,sizeof(sum));
//兩條邊合併進行dp
go(m1,0,1)for (ll m2=0;m1+m2<=1;m2++){
go(l,0,1)go(r,0,1){
if (a.dp[m1][l]==-1||b.dp[m2][r]==-1) continue;
updata(dp[l][r],sum[l][r],a.dp[m1][l]+b.dp[m2][r], (a.sum[m1][l]*b.sum[m2][r])%MOD);
}
}
sit=e[ne.u].find(ne); //查找map 看有沒有已經合併成l到r的邊
bool flag=(sit!=e[ne.u].end());
if (flag){ //若有再進行dp
ll dpf[2][2],sumf[2][2];
memset(dpf,-1,sizeof(dpf));
memset(sumf,-1,sizeof(sumf));
//什麼水平,這是什麼水平
//開始感覺這裏應該是memcpy(dpf,dp,sizeof(dp)); 後來發現更新是可以更新到的
go(l1,0,1) go(r1,0,1){
for (ll l2=0;l2+l1<=1;l2++)for (ll r2=0;r2+r1<=1;r2++){
if (dp[l1][r1]==-1||sit->dp[l2][r2]==-1) continue;
updata(dpf[l1+l2][r1+r2],sumf[l1+l2][r1+r2],dp[l1][r1]+sit->dp[l2][r2],(sum[l1][r1]*sit->sum[l2][r2])%MOD);
}
}
memcpy(dp,dpf,sizeof(dpf));
memcpy(sum,sumf,sizeof(sumf));
//又是什麼惡魔操作memcpy
e[ne.u].erase(ne);
swap(ne.u,ne.v);
e[ne.u].erase(ne);
swap(ne.u,ne.v);
//雙向是相同的不用再刪除一次
}
go(l,0,1) go(r,0,1){
ne.dp[l][r]=dp[l][r],ne.sum[l][r]=sum[l][r];
}
e[ne.u].insert(ne);
swap(ne.u,ne.v);
go(l,0,1) go(r,0,1){
ne.dp[l][r]=dp[r][l],ne.sum[l][r]=sum[r][l];
}
e[ne.u].insert(ne);
if (flag&&e[ne.u].size()==2) q.push(ne.u); //若產生了度爲2的點入棧
if (flag&&e[ne.v].size()==2) q.push(ne.v);
}
ll dp=-1;
ll sum=-1;
edge ne=*e[q.front()].begin();
go(l,0,1) go(r,0,1){
updata(dp,sum,ne.dp[l][r],ne.sum[l][r]);
}
printf("%lld %lld\n",dp,sum);
}
return 0;
}
/*
1
4 5
1 2 1
1 3 1
1 4 1
2 3 1
3 4 1
*/