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< ...
随机推荐
- css设置垂直居中方式总结
方式1:flex布局,display:flex;align-items:center <!DOCTYPE html> <html lang="en"> &l ...
- linux 增加虚拟内存swap(使用文件)
1.简介 如果你的服务器的总是报告内存不足,并且时常因为内存不足而引发服务被强制kill的话,在不增加物理内存的情况下,启用swap交换区作为虚拟内存是一个不错的选择. 为了测试一些功能我在阿里云购买 ...
- idea 打包springboot项目报错:404
1. 在本地的idea中,我们必须通过context-path(这里是volkswagen)+controller中的路径名访问项目,但是打包后,就不需要这个context-path,需要的是war包 ...
- 设置eclipse联想功能
当我们在用eclipse的时候,怎么能够让自己的编码速度加快?我想利用eclipse的提示功能是其中的方法之一.下面就利出配置eclipse联想功能(代码的提示功能)的步骤: 1. 打开Eclipse ...
- 【原创】Aduino小车玩法全记录
本来打算用一周时间好好研究下基于Arduino开发板的小车实验,结果实际上两天就完成了小车可玩的各种功能,包括完成特定动作组合,黑线循迹,(带后退)红外避障和(带舵机)超声波避障,超声波测距,红外遥控 ...
- SpringBoot+POI报表批量导出
由于servletResponse 获取的输出流对象在一次请求中只能输出一次,所以要想实现批量导出报表,需要将excel文件打包成zip格式然后输出. 好了,废话不多说,上代码. 1. 首先,需要导入 ...
- Python第八课学习
Python第八课学习 www.cnblogs.com/resn/p/5800922.html 1 Ubuntu学习 根 / /: 所有目录都在 /boot : boot配置文件,内核和其他 linu ...
- Ubuntu下useradd与adduser区别
Ubuntu下useradd与adduser有所不同 1.useradd在使用该命令创建用户是不会在/home下自动创建与用户名同名的用户目录,而且不会自动选择shell版本,也没有设置密码,那么这个 ...
- 数据分析处理库--Pandas
Pandas库: pandas索引与计算:
- jupyter notebook添加虚拟环境
原本以为,当进入虚拟环境之后,再运行jupyter notebook应该是这个环境下的jupyter,比如我默认创建一个文件,这个文件调用的编译器应该是这个虚拟环境中的编译器,实际上并不是 当你进入j ...