洛谷 5291 [十二省联考2019]希望(52分)——思路+树形DP
题目:https://www.luogu.org/problemnew/show/P5291
考场上写了 16 分的。不过只得了 4 分。
对于一个救援范围,其中合法的点集也是一个连通块。 2n 枚举一个救援范围,然后换根 DP 一下范围内的每个点开始的最长链,那些最长链 <=L 的点就是该范围的合法点集。
这样得到每个合法点集出现的方案, 与卷积 k 次即可。卷积的时候先 FWT 成点值,然后快速幂一样乘 k 次,再 FWT 回来即可。
但只有 4 分。过不了大样例。
#include<cstdio>
#include<cstring>
#include<algorithm>
#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 N=1e6+,mod=;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;} int n,L,k,hd[N],xnt,to[N<<],nxt[N<<];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
namespace S1{
const int K=,M=(<<)+;
int bin[K],dp[K],pr[K],sc[K],nd[K],tot;
int ts,len,f[M],g[M]; bool vis[K],col[K];
void chk_dfs(int cr,int fa)
{
vis[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa)chk_dfs(v,cr);
}
void dfs(int cr,int fa)
{
dp[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa)
dfs(v,cr), dp[cr]=Mx(dp[cr],dp[v]+);
}
void dfsx(int cr,int fa,int tmp)
{
if(Mx(dp[cr],tmp)<=L)ts|=bin[cr-];
int l=tot;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa) nd[++tot]=v;
int r=tot; if(l==r)return;
pr[l+]=dp[nd[l+]]+;
for(int i=l+;i<=r;i++)pr[i]=Mx(pr[i-],dp[nd[i]]+);
sc[r]=dp[nd[r]]+;
for(int i=r-;i>l;i--)sc[i]=Mn(sc[i+],dp[nd[i]]+);
for(int i=l+;i<=r;i++)
{
int tp=tmp;//=tmp
if(i>l+)tp=pr[i-];if(i<r)tp=Mx(tp,sc[i+]);
dfsx(nd[i],cr,tp+);
}
}
void fwt(int *a,bool fx)
{
for(int R=;R<=len;R<<=)
for(int i=,m=R>>;i<len;i+=R)
for(int j=;j<m;j++)
{
if(!fx)a[i+j]=upt(a[i+j]+a[i+m+j]);
else a[i+j]=upt(a[i+j]-a[i+m+j]);
}
}
void solve()
{
bin[]=;
for(int i=;i<=n;i++)bin[i]=bin[i-]<<;
for(int s=;s<bin[n];s++)
{
for(int i=;i<=n;i++)
{
vis[i]=;
if(s&bin[i-])col[i]=; else col[i]=;
}
int cr=;
for(int i=;i<=n;i++)
if(col[i]){chk_dfs(i,);cr=i;break;}
bool fg=;
for(int i=;i<=n;i++)
if(col[i]&&!vis[i]){fg=;break;}
if(fg)continue;
ts=tot=; dfs(cr,); dfsx(cr,,);
if(ts){f[ts]++; g[ts]++;}
}
k--; len=bin[n]; fwt(g,); fwt(f,);
while(k)
{
if(k&)
{
for(int i=;i<len;i++)f[i]=(ll)f[i]*g[i]%mod;
}
for(int i=;i<len;i++)g[i]=(ll)g[i]*g[i]%mod;
k>>=;
}
int ans=; fwt(f,);
for(int s=;s<bin[n];s++)ans=upt(ans+f[s]);
printf("%d\n",ans);
}
}
int main()
{
freopen("hope.in","r",stdin);
freopen("hope.out","w",stdout);
n=rdn();L=rdn();k=rdn();
for(int i=,u,v;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
if(n<=){S1::solve();return ;}
return ;
}
囧
后来发现两个地方写错了:
1.换根的时候做了前缀 max 和后缀 max ,其中后缀取 max 写成取 min 了;
2.往孩子换根的时候用了一个 tp 对父亲来的 tmp 、前缀 max 、后缀 max 取 max ,结果 tp=tmp 之后写成 tp = pr[ ] 而非 tp = Mx( tp , pr[ ] ) 。
改了这两个地方就有 16 分了。
希望以后写代码的时候更仔细。别走神或不集中之类的。
#include<cstdio>
#include<cstring>
#include<algorithm>
#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 N=1e6+,mod=;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;} int n,L,k,hd[N],xnt,to[N<<],nxt[N<<];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
namespace S1{
const int K=,M=(<<)+;
int bin[K],dp[K],pr[K],sc[K],nd[K],tot;
int ts,len,f[M],g[M]; bool vis[K],col[K];
void chk_dfs(int cr,int fa)
{
vis[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa)chk_dfs(v,cr);
}
void dfs(int cr,int fa)
{
dp[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa)
dfs(v,cr), dp[cr]=Mx(dp[cr],dp[v]+);
}
void dfsx(int cr,int fa,int tmp)
{
if(Mx(dp[cr],tmp)<=L)ts|=bin[cr-];
int l=tot;
for(int i=hd[cr],v;i;i=nxt[i])
if(col[v=to[i]]&&v!=fa) nd[++tot]=v;
int r=tot; if(l==r)return;
pr[l+]=dp[nd[l+]]+;
for(int i=l+;i<=r;i++)pr[i]=Mx(pr[i-],dp[nd[i]]+);
sc[r]=dp[nd[r]]+;
for(int i=r-;i>l;i--)sc[i]=Mx(sc[i+],dp[nd[i]]+);////mx not mn!!!
for(int i=l+;i<=r;i++)
{
int tp=tmp;//=tmp
if(i>l+)tp=Mx(tp,pr[i-]);if(i<r)tp=Mx(tp,sc[i+]);//mx!!!
dfsx(nd[i],cr,tp+);
}
}
void fwt(int *a,bool fx)
{
for(int R=;R<=len;R<<=)
for(int i=,m=R>>;i<len;i+=R)
for(int j=;j<m;j++)
{
if(!fx)a[i+j]=upt(a[i+j]+a[i+m+j]);
else a[i+j]=upt(a[i+j]-a[i+m+j]);
}
}
void solve()
{
bin[]=;
for(int i=;i<=n;i++)bin[i]=bin[i-]<<;
for(int s=;s<bin[n];s++)
{
for(int i=;i<=n;i++)
{
vis[i]=;
if(s&bin[i-])col[i]=; else col[i]=;
}
int cr=;
for(int i=;i<=n;i++)
if(col[i]){chk_dfs(i,);cr=i;break;}
bool fg=;
for(int i=;i<=n;i++)
if(col[i]&&!vis[i]){fg=;break;}
if(fg)continue;
ts=tot=; dfs(cr,); dfsx(cr,,);
if(ts){f[ts]++; g[ts]++;}
}
k--; len=bin[n]; fwt(g,); fwt(f,);
while(k)
{
if(k&)
{
for(int i=;i<len;i++)f[i]=(ll)f[i]*g[i]%mod;
}
for(int i=;i<len;i++)g[i]=(ll)g[i]*g[i]%mod;
k>>=;
}
int ans=; fwt(f,);
for(int s=;s<bin[n];s++)ans=upt(ans+f[s]);
printf("%d\n",ans);
}
}
int main()
{
freopen("hope.in","r",stdin);
freopen("hope.out","w",stdout);
n=rdn();L=rdn();k=rdn();
for(int i=,u,v;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
if(n<=){S1::solve();return ;}
return ;
}
然后参照题解写了 52 分的。
很重要的转化是令 \( f[i] \) 表示 i 是合法点的救援范围个数,那么 k 个救援范围包含 i 的方案就是 \( f[i]^k \) ;考虑到一个方案的合法点集是连通块,即点数比边数大一,所以令 \( g[i] \) 表示边 i 的两端点是合法点的救援范围个数,答案就是 \( \sum\limits_{i=1}^{n}f[i]^k - \sum\limits_{i=1}^{n-1}g[i]^k \) 。
然后就可以写 n*L 的 DP 了。再把链和 L=n 的部分做一下就有 52 分。
不太会 k=1 时候的长链剖分。
#include<cstdio>
#include<cstring>
#include<algorithm>
#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 N=1e6+,mod=;
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,L,k,hd[N],xnt=,to[N<<],nxt[N<<],rd[N],f[N],g[N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;rd[y]++;}
namespace S1{
const int N=;
int dfs(int cr,int fa,int lm)
{
int ret=; if(!lm)return ret;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
ret=(ll)ret*(dfs(v,cr,lm-)+)%mod;
return ret;
}
void dfsx(int cr,int fa)
{
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
int ret=dfs(cr,v,L-);
ret=(ll)ret*dfs(v,cr,L-)%mod;
g[i>>]=ret; dfsx(v,cr);
}
}
void solve()
{
for(int i=;i<=n;i++) f[i]=dfs(i,,L);
dfsx(,); int ans=;
for(int i=;i<=n;i++)ans=upt(ans+pw(f[i],k));
for(int i=;i<n;i++)ans=upt(ans-pw(g[i],k));
printf("%d\n",ans);
}
}
namespace S2{
const int N=1e5+,M=;
int nd[N],tot;
struct Node{
int v[M],s[M],cd;
void init(){v[]=s[]=;}
void frs()
{
for(int i=;i<=cd;i++)
s[i]=upt(s[i-]+v[i]);
}
void cz()
{
cd=Mn(cd+,L);
for(int i=cd;i;i--)v[i]=v[i-];
frs();
}
}dp[N],pr[N],sc[N],up[N];
void mrg(Node &d0,Node d1)
{
int yc=d0.cd, lm=d1.cd, tc=Mn(L,Mx(yc,lm+));
d0.cd=tc;
for(int j=yc+;j<=tc;j++)
d0.v[j]=, d0.s[j]=d0.s[yc];//0 not 1
for(int j=;j<=tc;j++)
{
int tp;
if(j-<=lm)tp=d1.s[j-]; else tp=d1.s[lm];
tp++;///for choosen't
d0.v[j]=(ll)d0.v[j]*tp%mod;
if(j-<=lm)
d0.v[j]=(d0.v[j]+(ll)d0.s[j-]*d1.v[j-])%mod;
}
d0.frs();
}
void mg2(Node &d0,Node d1)
{
int yc=d0.cd, lm=d1.cd, tc=Mn(L,Mx(yc,lm));
d0.cd=tc;
for(int j=yc+;j<=tc;j++)
d0.v[j]=, d0.s[j]=d0.s[yc];//0 not 1
for(int j=;j<=tc;j++)
{
int tp;
if(j<=lm)tp=d1.s[j]; else tp=d1.s[lm];
tp++;///for choosen't
d0.v[j]=(ll)d0.v[j]*tp%mod;
if(j&&j<=lm)
d0.v[j]=(d0.v[j]+(ll)d0.s[j-]*d1.v[j])%mod;
}
d0.frs();
}
void dfs(int cr,int fa)
{
dp[cr].init();
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs(v,cr);
mrg(dp[cr],dp[v]);
}
}
void dfsx(int cr,int fa)
{
int tp=up[cr].cd;
f[cr]=(tp>=L?up[cr].s[L]:up[cr].s[tp]);
tp=dp[cr].cd;
f[cr]=(ll)f[cr]*(tp>=L?dp[cr].s[L]:dp[cr].s[tp])%mod;
int l=tot;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
nd[++tot]=i;
if(tot==l+)pr[tot].init();
else pr[tot]=pr[tot-];
mrg(pr[tot],dp[v]);
}
int r=tot;
for(int i=r;i>l;i--)
{
if(i==r)sc[i].init();
else sc[i]=sc[i+];
mrg(sc[i],dp[to[nd[i]]]);
}
for(int i=l+;i<=r;i++)
{
pr[i].v[]=pr[i].s[]=;pr[i].frs();
sc[i].v[]=sc[i].s[]=;sc[i].frs();
}
for(int i=l+;i<=r;i++)
{
int v=to[nd[i]],bh=nd[i]>>;
up[v]=up[cr];
if(i>l+) mg2(up[v],pr[i-]);
if(i<r) mg2(up[v],sc[i+]);
int tp=up[v].cd;
g[bh]=(tp>=L-?up[v].s[L-]:up[v].s[tp]);
tp=dp[v].cd;
g[bh]=(ll)g[bh]*(tp>=L-?dp[v].s[L-]:dp[v].s[tp])%mod;
up[v].cz();
dfsx(v,cr);
}
}
void solve()
{
dfs(,); up[].init(); dfsx(,);
int ans=;
for(int i=;i<=n;i++)
ans=upt(ans+pw(f[i],k));
for(int i=;i<n;i++)
ans=upt(ans-pw(g[i],k));
printf("%d\n",ans);
}
}
namespace S3{
const int N=2e5+;
int dp[N],nd[N],pr[N],sc[N],tot;
void dfs(int cr,int fa)
{
dp[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs(v,cr); dp[cr]=(ll)dp[cr]*(dp[v]+)%mod;
}
}
void dfsx(int cr,int fa,int tmp)
{
f[cr]=(ll)dp[cr]*(tmp+)%mod;
int l=tot;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
nd[++tot]=i;
if(tot==l+)pr[tot]=;
else pr[tot]=pr[tot-];
pr[tot]=(ll)pr[tot]*(dp[v]+)%mod;
}
int r=tot;
for(int i=r;i>l;i--)
{
if(i==r)sc[i]=;
else sc[i]=sc[i+];
sc[i]=(ll)sc[i]*(dp[to[nd[i]]]+)%mod;
}
for(int i=l+;i<=r;i++)
{
int v=to[nd[i]], tp=tmp+, bh=nd[i]>>;
if(i>l+)tp=(ll)tp*pr[i-]%mod;
if(i<r)tp=(ll)tp*sc[i+]%mod;
g[bh]=(ll)tp*dp[v]%mod;
dfsx(v,cr,tp);
}
}
void solve()
{
dfs(,); dfsx(,,); int ans=;
for(int i=;i<=n;i++)
ans=upt(ans+pw(f[i],k));
for(int i=;i<n;i++)
ans=upt(ans-pw(g[i],k));
printf("%d\n",ans);
}
}
namespace S4{
void solve()
{
int ans=;
for(int i=;i<=n;i++)
{
int t0=Mn(L+,i), t1=Mn(L+,n-i+);
ans=upt(ans+pw((ll)t0*t1%mod,k));
}
for(int i=;i<n;i++)
{
int t0=Mn(L,i), t1=Mn(L,n-i);
ans=upt(ans-pw((ll)t0*t1%mod,k));
}
printf("%d\n",ans);
}
}
int main()
{
n=rdn();L=rdn();k=rdn();
for(int i=,u,v;i<n;i++)
{ u=rdn();v=rdn();add(u,v);add(v,u);}
if(n<=){S1::solve();return ;}
if((ll)n*L<=1e7){S2::solve();return ;}
if(L==n){S3::solve();return ;}
bool fg=;
for(int i=;i<=n;i++)if(rd[i]>){fg=;break;}
if(!fg){S4::solve();return ;}
return ;
}
洛谷 5291 [十二省联考2019]希望(52分)——思路+树形DP的更多相关文章
- 洛谷.5284.[十二省联考2019]字符串问题(后缀自动机 拓扑 DP)
LOJ BZOJ 洛谷 对这题无话可说,确实比较...裸... 像dls说的拿拓扑和parent树一套就能出出来了... 另外表示BZOJ Rank1 tql... 暴力的话,由每个\(A_i\)向它 ...
- LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分
传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...
- 洛谷.5283.[十二省联考2019]异或粽子(可持久化Trie 堆)
LOJ 洛谷 考场上都拍上了,8:50才发现我读错了题=-= 两天都读错题...醉惹... \(Solution1\) 先求一遍前缀异或和. 假设左端点是\(i\),那么我们要在\([i,n]\)中找 ...
- 洛谷P5289 [十二省联考2019]皮配(01背包)
啊啊啊边界判错了搞死我了QAQ 这题是一个想起来很休闲写起来很恶心的背包 对于\(k=0\)的情况,可以发现选阵营和选派系是独立的,对选城市选阵营和学校选派系分别跑一遍01背包就行了 对于\(k> ...
- 洛谷P5284 [十二省联考2019]字符串问题 [后缀树]
传送门 思路 设\(dp_i\)表示以\(i\)结尾的\(A\)串,能达到的最长长度. 然后发现这显然可以\(i\)往自己控制的\(k\)连边,\(k\)往能匹配的\(j\)连边,就是个最长路,只要建 ...
- 洛谷P5284 [十二省联考2019]字符串问题(SAM+倍增+最长路)
题面 传送门 题解 首先,我们把串反过来,那么前缀就变成后缀,建一个\(SAM\).我们发现一个节点的后缀是它的所有祖先 那么我们是不是直接按着\(parent\)树建边就可以了呢? 显然不是.我们假 ...
- 题解 loj3050 「十二省联考 2019」骗分过样例
CASE \(1\sim 3\) \(n\)组测试数据,每次输入一个数\(x\),求\(19^x\). 测试点\(1\),\(x=0,1,\dots n-1\),可以直接递推. 测试点\(2\)要开l ...
- luogu P5291 [十二省联考2019]希望
luogu loj 无论最终结果将人类历史导向何处 \(\quad\)我们选择 \(\quad\quad\)\(\large{希望}\) 诶我跟你讲,这题超修咸的 下面称离连通块内每个点距离不超过\( ...
- 【题解】Luogu P5291 [十二省联考2019]希望
ytq鸽鸽出的题真是毒瘤 原题传送门 题目大意: 有一棵有\(n\)个点的树,求有多少方案选\(k\)个联通块使得存在一个中心点\(p\),所有\(k\)个联通块中所有点到\(p\)的距离都\(\le ...
随机推荐
- Android修行之路------List view无法获取监听方法
注意: 1.在list view自定义布局中如果添加滚动布局,会导致自定义布局无法获取监听. 2.如果ListView的每项布局里有像Button,ImageButton之类View的控键时,这些Vi ...
- ubantu安装python3虚拟环境
Ubuntu安装python3虚拟环境 安装虚拟环境 步骤: 打开Linux终端(快捷键Ctrl+Alt+T),输入命令: sudo apt install python-virtualenv sud ...
- hive中left join、left outer join和left semi join的区别
先说结论,再举例子. hive中,left join与left outer join等价. left semi join与left outer join的区别:left semi join相当 ...
- eclipse 设置Java快捷键补全
打开Eclipse,点击Window--Preferences--Java--Editor--ContentAssist Auto Activation 勾选Enable auto activatio ...
- java.lang.OutOfMemoryError: GC overhead limit exceeded
前端请求:{"code":400,"message":"Handler dispatch failed; nested exception is ja ...
- Fedora 10编程开发工具
1请问Fedora 10编程开发工具有什么 编辑器就用vim,编译用gcc,当然个人爱好随意 IDE的话推荐eclipse,如果做C/C++的,用codeblocks也是个不错的选择 输入gcc -v ...
- 本周java学习
本周学习的内容让我又进一步实践了java语言,我本周学到的内容是 循环: 强制结束命令行 //Ctrl+c for 循环的无限循环形式: for( ; ; )() while循环的无限循环形式: ...
- Python 属性
class Person: def __init__(self, name, gender, birth): self.name = name self.gender = gender self.bi ...
- HDU 6038 17多校1 Function(找循环节/环)
Problem Description You are given a permutation a from 0 to n−1 and a permutation b from 0 to m−1. D ...
- 记第二届CCPC全国女生赛参赛体验
离比赛时间已经有点久了,今天整理博客看到“”参赛体会“”这一分类,觉得记录一下也好 流水账记一下感受 因为题目我已经忘记了.. 第一次..那么久..大概有三个多小时在金牌区吧.. 然后就是一无所出了. ...