LOJ 2547 「JSOI2018」防御网络——思路+环DP
题目:https://loj.ac/problem/2547
一条树边 cr->v 会被计算 ( n-siz[v] ) * siz[v] 次。一条环边会被计算几次呢?于是去写了斯坦纳树。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int mod=1e9+;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;}
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;} int n,m;
namespace S1{
const int N=,M=(<<)+;
int hd[N],xnt,to[N<<],nxt[N<<];
int bin[N],dp[N][M],dis[N][N]; bool vis[N];
queue<int> q;
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void init()
{
for(int i=;i<=n;i++)
{
memset(vis,,sizeof vis);
q.push(i); vis[i]=;
while(q.size())
{
int k=q.front(); q.pop();
for(int i=hd[k],v;i;i=nxt[i])
if(!vis[v=to[i]])
{ vis[v]=;dis[i][v]=dis[i][k]+;q.push(v);}
}
}
bin[]=;for(int i=;i<=n;i++)bin[i]=bin[i-]<<;
}
void solve()
{
for(int i=,u,v;i<=m;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
init();
memset(dp,0x3f,sizeof dp);
for(int i=;i<=n;i++)dp[i][bin[i-]]=;
int ans=;
for(int s=;s<bin[n];s++)
{
for(int i=;i<=n;i++)
for(int t=(s-)&s;t;t=(t-)&s)
dp[i][s]=Mn(dp[i][s],dp[i][t]+dp[i][s^t]);
for(int i=;i<=n;i++)q.push(i),vis[i]=;
while(q.size())
{
int k=q.front(); q.pop(); vis[k]=;
for(int i=hd[k],v;i;i=nxt[i])
if(dp[v=to[i]][s]>dp[k][s]+)
{
dp[v][s]=dp[k][s]+;
if(!vis[v])q.push(v),vis[v]=;
}
}
int mn=dp[][s];
for(int i=;i<=n;i++)mn=Mn(mn,dp[i][s]);
ans=upt(ans+mn);
}
ans=(ll)ans*pw(bin[n],mod-)%mod;
printf("%d\n",ans);
}
}
int main()
{
n=rdn();m=rdn();
if(n<=){S1::solve();return ;}
return ;
}
不应从每条环边的角度考虑,而要从每个环的角度考虑。思维还是不足。
想算一个环上有 len 条边被选的方案。
记一个“环外子树(含自己这点)中有点被选的环上点” 为 “被选的点” 。
首先考虑如果有一个选点方案,这个环会被怎么选边。为了把点都连通,被选的点两两之间应该连通。
所以环上没被选的边一定是最远的两个相邻的被选的点之间的部分。
想求一个环 “最远相邻被选点的间隔为 len ” 的方案。注意到 “最远相邻被选点的间隔 <=len ”的方案容易 DP 。
断环成链,dp[ i ][ j ] 表示 i 点和 j 点要选,[ i , j ] 之间被选点间隔 <=len 的方案。则 \( dp[i][j]=f[j]*\sum\limits_{k=j-len}^{j-1}dp[i][k] \) ,其中 \( f[j] \) 是选 j 点的方案,即 ( 2环外子树大小 -1 ) 。
前缀和优化即可。对于一个 len ,合法的 dp[ i ][ j ] 应该满足 (cnt - j) + i <=len (cnt 是环点个数)。把这些加到 g[ len ] 上,用 (cnt-len) * (g[ len ] - g[ len-1 ]) 贡献答案即可。
求环外子树大小要小心。注意清空数组。注意分辨不同的环。可以给环最浅的点打标记,特殊求该点的环外子树大小。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=,M=N<<,mod=1e9+;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;}
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;} int n,m,hd[N],xnt,to[M],nxt[M],spe[N];
int dp[N],pr[N],f[N],g[N],siz[N],ans,bin[N];
int tim,dfn[N],low[N],sta[N],top,cnt,col[N],qnt,q[N];
bool vis[N],ins[N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void tarjan(int cr,int fa)
{
dfn[cr]=low[cr]=++tim; sta[++top]=cr; ins[cr]=;
siz[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
if(!dfn[v])
{tarjan(v,cr);low[cr]=Mn(low[cr],low[v]);siz[cr]+=siz[v];}
else if(ins[v])low[cr]=Mn(low[cr],dfn[v]);
}
if(dfn[cr]==low[cr])
{
if(sta[top]==cr){ins[cr]=;top--;return;}
cnt++;
do{int v=sta[top];ins[v]=;col[v]=cnt;}while(sta[top--]!=cr);
spe[cr]=fa;//fa can be 0 but no influence
}
}
void dfsx(int cr)
{
q[++qnt]=cr; vis[cr]=; int id=qnt;
for(int i=hd[cr],v;i;i=nxt[i])
if(!vis[v=to[i]]&&col[v]==col[cr])dfsx(v);//col==
else if(col[v]!=col[cr]&&v!=spe[cr])//include !col
f[id]+=siz[v];//col!= not !col//v!=spe[cr]
if(spe[cr])
f[id]+=n-siz[cr];//+= not =//n-siz[cr] not spe-siz[cr]
f[id]++;
}
void solve(int cr)
{
for(int i=;i<=qnt;i++)f[i]=; qnt=;//f[i]=0//qnt=0
dfsx(cr);
for(int len=;len<=qnt;len++)
{
g[len]=;
for(int i=;i<=len;i++)
{
dp[i]=bin[f[i]]; pr[i]=dp[i]; pr[i-]=;
int lm=qnt-len+i;
if(i>=lm)g[len]=upt(g[len]+dp[i]);
for(int j=i+;j<=qnt;j++)
{
dp[j]=(ll)upt(pr[j-]-pr[Mx(i,j-len)-])*bin[f[j]]%mod;
pr[j]=upt(pr[j-]+dp[j]);
if(j>=lm)g[len]=upt(g[len]+dp[j]);
}
}
}
for(int len=;len<qnt;len++)
ans=(ans+(ll)(qnt-len)*upt(g[len]-g[len-]))%mod;
}
void dfs(int cr)
{
if(col[cr]&&!vis[cr])solve(cr); ins[cr]=;//not use vis again
for(int i=hd[cr],v;i;i=nxt[i])
if(!ins[v=to[i]])
{
if(col[cr]!=col[v]||!col[cr])
//col!= not !col||!col//also !col
ans=(ans+(ll)bin[siz[v]]*bin[n-siz[v]])%mod;
dfs(v);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=,u,v;i<=m;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
bin[]=;for(int i=;i<=n;i++)bin[i]=upt(bin[i-]<<);
for(int i=;i<=n;i++)bin[i]=upt(bin[i]-);//
tarjan(,); dfs();
ans=(ll)ans*pw(bin[n]+,mod-)%mod;
printf("%d\n",ans);
return ;
}
LOJ 2547 「JSOI2018」防御网络——思路+环DP的更多相关文章
- 【LOJ】 #2547. 「JSOI2018」防御网络
题解 如果只是一棵树的话,那么就枚举每条边,分成两部分大小为\(a\)和\(b\) 那么这条边被统计的方案数是\((2^a - 1)(2^b - 1)\) 如果是一个环的话,我们枚举环上至少有\(N ...
- LOJ #2547 Luogu P4517「JSOI2018」防御网络
好像也没那么难写 LOJ #2547 Luogu P4517 题意 在一棵点仙人掌中等概率选择一个点集 求选出点集的斯坦纳树大小的期望 定义点仙人掌为不存在一个点在多个简单环中的连通图 斯坦纳树为在原 ...
- LOJ 2550 「JSOI2018」机器人——找规律+DP
题目:https://loj.ac/problem/2550 只会写20分的搜索…… #include<cstdio> #include<cstring> #include&l ...
- LOJ 2548 「JSOI2018」绝地反击 ——二分图匹配+网络流手动退流
题目:https://loj.ac/problem/2548 如果知道正多边形的顶点,就是二分答案.二分图匹配.于是写了个暴力枚举多边形顶点的,还很愚蠢地把第一个顶点枚举到 2*pi ,其实只要 \( ...
- LOJ 2551 「JSOI2018」列队——主席树+二分
题目:https://loj.ac/problem/2551 答案是排序后依次走到 K ~ K+r-l . 想维护一个区间排序后的结果,使得可以在上面二分.求和:二分可以知道贡献是正还是负. 于是想用 ...
- LOJ 2546 「JSOI2018」潜入行动——树形DP
题目:https://loj.ac/problem/2546 dp[ i ][ j ][ 0/1 ][ 0/1 ] 表示 i 子树,用 j 个点,是否用 i , i 是否被覆盖. 注意 s1<= ...
- LOJ 3093 「BJOI2019」光线——数学+思路
题目:https://loj.ac/problem/3093 考虑经过种种反射,最终射下去的光线总和.往下的光线就是这个总和 * a[ i ] . 比如只有两层的话,设射到第二层的光线是 lst ,那 ...
- LOJ 2541 「PKUWC2018」猎人杀——思路+概率+容斥+分治
题目:https://loj.ac/problem/2541 看了题解才会……有三点很巧妙. 1.分母如果变动,就很不好.所以考虑把操作改成 “已经选过的人仍然按 \( w_i \) 的概率被选,但是 ...
- LOJ 3089 「BJOI2019」奥术神杖——AC自动机DP+0/1分数规划
题目:https://loj.ac/problem/3089 没想到把根号之类的求对数变成算数平均值.写了个只能得15分的暴力. #include<cstdio> #include< ...
随机推荐
- 互动科技 快乐分享 X/Open DTP——分布式事务模型
这一几天一直在回顾事务相关的知识,也准备把以前了解皮毛的知识进行一些深入总结,虽然这一些知识并没有用到,但是了解其实现原理还是很有必要的,因为知道了原理,你也能把它实现出来. 在上一节事务的编程模型里 ...
- jdbc工具类的封装,以及表单验证数据提交后台
在之前已经写过了jdbc的工具类,不过最近学习了新的方法,所以在这里重新写一遍,为后面的javaEE做铺垫: 首先我们要了解javaEE项目中,文件构成,新建一个javaEE项目,在项目中,有一个we ...
- 【sklearn入门】通过sklearn实现k-means并可视化聚类结果
import numpy as np from sklearn.cluster import KMeans from mpl_toolkits.mplot3d import Axes3D import ...
- Delphi7第三方控件
控件安装(安装时建议先关闭Delphi) 1.只有一个DCU文件的组件. DCU文件是编译好的单元文件,这样的组件是作者不想把源码公布.一般来说,作者必须说明此组件适合Delphi的哪种版本,如果版本 ...
- 作为程序员你不知道中国互联网300强你就OUT了!
很多程序员们工作一段时间之后,都想跳槽到综合实力更强的公司以获取高薪资或者接触到前沿的技术,但是哪些行业值得我们投入大量的时间,哪些公司属于我们行业中的佼佼者呢, 那么小编给您分析下2018年中国互联 ...
- asp。net内置委托
Action与Func是APS.NET内置委托 //--------------无返回值的委托Action--------------------------- Action是无返回值的泛型委托 Ac ...
- storm入门基础实例(无可靠性保证实例)
本实例为入门篇无可靠性保证实例,关于storm的介绍,以及一些术语名词等,可以参考Storm介绍(一).Storm介绍(二). 本案例是基于storm0.9.3版本 1.案例结构 案例:Word Co ...
- Sqlite 参数化 模糊查询 解决方案
转自:https://codedefault.com/2018/does-dapper-support-the-like-operator-in-csharp-application 问题描述 如题, ...
- python 读fnl数据
(1) FNL 数据介绍 FNL((Final Operational Global Analysis)数据是美国国家环境预报中心(NECP)/美国国家大气研究中心(NCAR)提供的全球再分析资料,空 ...
- linux 在后台常驻运行php脚本
php a.php &