题目

建立一个二分图,左右各n个点,在左边的第x个点和右边的第y个点之间连一条权值为\(a_{x,y}\)的边。根据“积和式”的定义,我们是要在矩阵中选择n个位置,满足任意两个位置不共行、不共列,并计算所有选择方案的对应位置的值的积之和。显然,每一种选位置的方案对应这个二分图的一个完美匹配,我们现在要计算这个二分图每一个完美匹配的匹配边的边权乘积之和。左边和右边都有1e5个点,一共1e10条边,这是很多的;但是边权不是1的边只有50条,这就很好,考虑怎么略去边权为1的边的计算。对于每条\(a_{x,y}>1\)的边,令其中\(a_{x,y}-1\)条为特殊边,剩下1条为普通边。枚举所有选了特殊边的匹配点对,方案数\(\prod (a_{x,y}-1)\);剩下的每队匹配点就只有1种选择了,也就是选普通边,方案数\((n-左边的选择了特殊边的匹配点个数)\)!,因为左边剩下的点都可以在右边的未匹配点中任选一个,且每对只有1种方案。考虑仅由\(a_{x,y}>1\)的边构成的图,注意到每个连通块的方案数是不互相影响的,对于一个连通块,可以计算其中有\(i\)对点选择用特殊边匹配的方案数为\(f_i\),然后把每个连通块的f数组手动暴力卷积起来,最后乘上剩下节点数量的阶乘。

接下来对每个连通块分别计算。注意到连通块内点数是不超过边数+1的,所以一个连通块内最多51个点,记连通块内总点数为k。那么,左边和右边一定有一边的点数是\(\leq \frac k2\)的,假设左边是点少的那边。那么可以令\(dp_{i,msk}\)表示当前遍历到右边的第i个点,左边已经被匹配掉的点的集合是msk,的所有方案的贡献之和。转移就枚举右边第i个点和左边哪个点匹配,或者干脆不匹配。最后\(dp_{sz(l),msk}\)中msk二进制里1的个数就是匹配掉的点对数。由于一共50条边,所以i那维和转移的枚举合起来最多50次,这部分的总复杂度也就是\(50\cdot 2^{25}\),但是25还是比较大的(虽然这样好像能过?),我们可以在\(sz(l)<20\)的时候这么干,对其他情况采用另一种办法。

在\(sz(l)>19\)的时候,总点数也就至少是38,由于边数最多50,所以如果我们求出这个图的一个生成树,非树边最多13条。接下来就可以\(2^{13}\)次枚举非树边的选法,然后在生成树上进行一个树形dp。具体来说,\(dp_{i,j,0/1}\)表示以i为根的子树,子树内有j对点选了特殊边匹配,以及i是否被特殊边匹配占用。转移比较简单,需要在i的儿子中再跑一个背包。这部分复杂度是\(2^{13}\cdot 50^2\),50是平方不是3次方的原因是转移时另外跑一个背包的时候,枚举了i的一个儿子子树中选择的匹配点对数和其他前缀儿子中匹配点对数总和,但是发现树中每2个节点只会在他们的LCA处产生一次枚举。

这样把这两个做法结合一下就可以轻松通过了~

下面的代码有小错,在CF上是WA的,但是模拟赛第三方数据可以通过,大家先将就着看吧(

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair using namespace std; const LL MOD=1e9+7; LL n,k,fac[100010],val[100],dp[60][530000],mark[100],dp2[60][60][2],dp3[60][60][2],sz[60];
map <pii,LL> mp,te;
map <LL,LL> vs;
vector <pii> g[200010],gg[100];
vector <LL> res,l,r,all;
bool vis[200010];
vector <pair <pii,LL> > e,nte; void dfs(LL pos)
{
if(pos<n) l.pb(pos);else r.pb(pos);
vis[pos]=true;
rep(i,g[pos].size())
{
e.pb(mpr(mpr(min(pos,g[pos][i].fi),max(pos,g[pos][i].fi)),g[pos][i].se));
if(!vis[g[pos][i].fi]) dfs(g[pos][i].fi),te[mpr(min(pos,g[pos][i].fi),max(pos,g[pos][i].fi))]=1;
}
} vector <LL> convolution(vector <LL> a,vector <LL> b)
{
vector <LL> c(a.size()+b.size()-1,0);
rep(i,a.size()) rep(j,b.size()) (c[i+j]+=a[i]*b[j])%=MOD;
return c;
} LL getid(LL x){return lower_bound(all.begin(),all.end(),x)-all.begin();} void dfs2(LL pos,LL par)
{
vector <pii> son;
sz[pos]=1;
rep(i,gg[pos].size()) if(gg[pos][i].fi!=par)
{
dfs2(gg[pos][i].fi,pos);
son.pb(gg[pos][i]);sz[pos]+=sz[gg[pos][i].fi];
}
rep(i,son.size()+3) rep(j,all.size()+3) rep(k,2) dp3[i][j][k]=0;
dp3[0][0][0]=1;
rep(i,son.size()) rep(j,all.size()+1) rep(k,2) if(dp3[i][j][k]>0)
{
rep(jj,sz[son[i].fi]) rep(kk,2) if(dp2[son[i].fi][jj][kk]>0)
{
(dp3[i+1][j+jj][k]+=dp3[i][j][k]*dp2[son[i].fi][jj][kk])%=MOD;
if(k==0&&kk==0&&mark[pos]==0&&mark[son[i].fi]==0) (dp3[i+1][j+jj+1][1]+=dp3[i][j][k]*dp2[son[i].fi][jj][kk]%MOD*son[i].se)%=MOD;
}
}
rep(j,all.size()+1) rep(k,2) dp2[pos][j][k]=dp3[son.size()][j][k];
} int main()
{
//freopen("c.in","r",stdin);
//freopen("c.out","w",stdout);
fac[0]=1;repn(i,100005) fac[i]=fac[i-1]*i%MOD;
cin>>n>>k;
LL x,y,z;
rep(i,k)
{
cin>>x>>y>>z;--x;--y;
mp[mpr(x,y)]=z;vs[x]=vs[y+n]=1;
}
for(auto it:mp)
{
g[it.fi.fi].pb(mpr(it.fi.se+n,it.se-1));
g[it.fi.se+n].pb(mpr(it.fi.fi,it.se-1));
}
res.pb(1);
for(auto it:vs) if(!vis[it.fi])
{
rep(i,99) val[i]=0;
e.clear();l.clear();r.clear();te.clear();
dfs(it.fi);
sort(e.begin(),e.end());e.erase(unique(e.begin(),e.end()),e.end());
if(min(l.size(),r.size())<=19)
{
if(r.size()<l.size()) swap(l,r);
rep(i,99) gg[i].clear();
rep(i,l.size())
{
rep(j,e.size()) if(e[j].fi.fi==l[i]||e[j].fi.se==l[i])
{
int v=e[j].fi.fi+e[j].fi.se-l[i],id;
rep(j,r.size()) if(r[j]==v) id=j;
gg[id].pb(mpr(i,e[j].se));
}
}
rep(j,59) rep(k,1<<l.size()) dp[j][k]=0;
dp[0][0]=1;
rep(i,r.size()) rep(k,1<<l.size()) if(dp[i][k]>0)
{
(dp[i+1][k]+=dp[i][k])%=MOD;
rep(p,gg[i].size()) if((k&(1<<gg[i][p].fi))==0) (dp[i+1][k|(1<<gg[i][p].fi)]+=dp[i][k]*gg[i][p].se)%=MOD;
}
rep(k,1<<l.size()) (val[__builtin_popcount(k)]+=dp[r.size()][k])%=MOD;
}
else
{
rep(i,99) gg[i].clear();
nte.clear();
all.clear();rep(i,l.size()) all.pb(l[i]);rep(i,r.size()) all.pb(r[i]);
sort(all.begin(),all.end());
rep(i,e.size())
{
if(te.find(e[i].fi)!=te.end())
{
LL u=getid(e[i].fi.fi),v=getid(e[i].fi.se);
gg[u].pb(mpr(v,e[i].se));gg[v].pb(mpr(u,e[i].se));
}
else nte.pb(mpr(mpr(getid(e[i].fi.fi),getid(e[i].fi.se)),e[i].se));
}
rep(i,all.size()) mark[i]=0;
rep(i,1<<nte.size())
{
LL addn=__builtin_popcount(i),mulv=1,bad=0;
rep(j,nte.size()) if((i&(1<<j))>0)
{
(mulv*=nte[j].se)%=MOD;
if(mark[nte[j].fi.fi]||mark[nte[j].fi.se]) bad=1;
mark[nte[j].fi.fi]=mark[nte[j].fi.se]=1;
}
if(bad) continue;
rep(j,all.size()) rep(k,all.size()) rep(p,2) dp2[j][k][p]=0;
dfs2(0,-1);
rep(j,all.size()+1) rep(k,2) if(dp2[0][j][k]>0) (val[j+addn]+=dp2[0][j][k]*mulv)%=MOD;
}
}
int lst=-1;rep(i,99) if(val[i]>0) lst=i;
if(lst==-1) continue;
vector <LL> tmp;rep(i,lst+1) tmp.pb(val[i]);
res=convolution(res,tmp);
}
LL ans=0;
rep(i,res.size()) (ans+=res[i]*fac[n-i])%=MOD;
cout<<ans<<endl;
return 0;
}

[题解] Codeforces 468 E Permanent 折半,DP,图论的更多相关文章

  1. 【题解】284E. Coin Troubles(dp+图论建模)

    [题解]284E. Coin Troubles(dp+图论建模) 题意就是要你跑一个完全背包,但是要求背包的方案中有个数相对大小的限制 考虑一个\(c_i<c_j\)的限制,就是一个\(c_i\ ...

  2. DP&图论 DAY 6 下午 考试

    DP&图论  DAY 6  下午  考试 样例输入 样例输出 题解 >50 pt      dij 跑暴力 (Floyd太慢了QWQ    O(n^3)) 枚举每个点作为起点,dijks ...

  3. Codeforces 909 C. Python Indentation (DP+树状数组优化)

    题目链接:Python Indentation 题意: Python是没有大括号来标明语句块的,而是用严格的缩进来体现.现在有一种简化版的Python,只有两种语句: (1)'s'语句:Simple ...

  4. DP&图论 DAY 7 上午

    DP&图论  DAY 7  上午 图论练习题 P2176 [USACO14FEB]路障Roadblock 先跑最短路(最多n条边,否则出环) 枚举每条边,加倍,再跑 dijkstra 取最大 ...

  5. DP&图论 DAY 6 上午

    DP&图论  DAY 6  上午 双连通分量 从u-->v不存在必经边,点 点双连通分量 边双连通分量 点/边双连通分量缩点之后变成一个树 找连通块的时候不越过割点或者桥 P3469 [ ...

  6. DP&图论 DAY 5 下午

    DP&图论  DAY 5  下午 树链剖分  每一条边要么属于重链要么轻边 证明: https://www.cnblogs.com/sagitta/p/5660749.html 轻边重链都是交 ...

  7. DP&图论 DAY 5 上午

    DP&图论  DAY 5  上午 POJ 1125 Stockbroker Grapevine 有 N 个股票经济人可以互相传递消息,他们之间存在一些单向的通信路径.现在有一个消息要由某个人开 ...

  8. DP&图论 DAY 4 下午图论

    DP&图论  DAY 4  下午 后天考试不考二分图,双联通 考拓扑排序 图论 图的基本模型 边: 有向边构成有向图 无向边构成无向图 权值: 1.无权 2.点权 3.边权 4.负权(dij不 ...

  9. DP&图论 DAY 4 上午

    DP&图论  DAY 4  上午 概率与期望 概率◦某个事件A发生的可能性的大小,称之为事件A的概率,记作P(A).◦假设某事的所有可能结果有n种,每种结果都是等概率,事件A涵盖其中的m种,那 ...

随机推荐

  1. hive常用函数 wordCount--Hive窗口函数1.1.1 聚合开窗函数聚合开窗函数实战

    第三天笔记 第三天笔记 SQL练习Hive 常用函数关系运算数值计算条件函数日期函数重点!!!字符串函数Hive 中的wordCount1.1 Hive窗口函数1.1.1 聚合开窗函数聚合开窗函数实战 ...

  2. 【高并发】通过源码深度分析线程池中Worker线程的执行流程

    大家好,我是冰河~~ 在<高并发之--通过ThreadPoolExecutor类的源码深度解析线程池执行任务的核心流程>一文中我们深度分析了线程池执行任务的核心流程,在ThreadPool ...

  3. ASP.NET Core 6框架揭秘实例演示[30]:利用路由开发REST API

    借助路由系统提供的请求URL模式与对应终结点之间的映射关系,我们可以将具有相同URL模式的请求分发给与之匹配的终结点进行处理.ASP.NET的路由是通过EndpointRoutingMiddlewar ...

  4. 【Harmony OS】【ArkUI】ets开发 简易视频播放器

    ​前言:这一次我们来使用ets的Swiper组件.List组件和Video组件制作一个简易的视频播放器.本篇是以HarmonyOS官网的codelab简易视频播放器(eTS)为基础进行编写.本篇最主要 ...

  5. .NET中MongoDB之CRUD

    参考文档 https://docs.mongoing.com/mongodb-crud-operations https://docs.mongodb.com/manual/crud/ https:/ ...

  6. 倒计时0日!Apache DolphineScheduler4月 Meetup 大佬手把手教你大数据开发,离线调度

    随着互联网技术和信息技术的发展,信息的数据化产生了许多无法用常规工具量化.处理和捕捉的数字信息.面对多元的数据类型,海量的信息价值,如何有效地对大数据进行挖掘分析,对大数据工作流进行调度,是保障企业大 ...

  7. 我就获取个时间,机器就down了

    本文主要讲解linux 时间管理系统中的一个问题 背景:linux 时间管理,包含clocksource,clockevent,timer,tick,timekeeper等等概念 , 这些概念有机地组 ...

  8. 国家都给NISP证书的补贴了!关于NISP考试的政策有哪些?

    NISP证书由中国信息安全测评中心依据中编办赋予"信息安全服务和信息安全专业人员的能力评估与资质审核"的职能而推出的证书,是中国信息安全测评中心代表国家实施的信息安全人员能力评定证 ...

  9. 【python】pandas 索引操作

    选择.修改数据(单层索引) 推荐使用.at..iat..loc..iloc 操作 句法 结果 备注 选择列 df[col] Series 基于列名(列的标签),返回Series 用标签选择行 df.l ...

  10. k8s命令补全方法

    正常安装了k8s后,使用kubect 工具后接的命令不能直接tab补全 命令补全方法: yum -y install bash-completionsource /usr/share/bash-com ...