神特么图论题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
*/