题目: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的更多相关文章

  1. 洛谷.5284.[十二省联考2019]字符串问题(后缀自动机 拓扑 DP)

    LOJ BZOJ 洛谷 对这题无话可说,确实比较...裸... 像dls说的拿拓扑和parent树一套就能出出来了... 另外表示BZOJ Rank1 tql... 暴力的话,由每个\(A_i\)向它 ...

  2. LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分

    传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...

  3. 洛谷.5283.[十二省联考2019]异或粽子(可持久化Trie 堆)

    LOJ 洛谷 考场上都拍上了,8:50才发现我读错了题=-= 两天都读错题...醉惹... \(Solution1\) 先求一遍前缀异或和. 假设左端点是\(i\),那么我们要在\([i,n]\)中找 ...

  4. 洛谷P5289 [十二省联考2019]皮配(01背包)

    啊啊啊边界判错了搞死我了QAQ 这题是一个想起来很休闲写起来很恶心的背包 对于\(k=0\)的情况,可以发现选阵营和选派系是独立的,对选城市选阵营和学校选派系分别跑一遍01背包就行了 对于\(k> ...

  5. 洛谷P5284 [十二省联考2019]字符串问题 [后缀树]

    传送门 思路 设\(dp_i\)表示以\(i\)结尾的\(A\)串,能达到的最长长度. 然后发现这显然可以\(i\)往自己控制的\(k\)连边,\(k\)往能匹配的\(j\)连边,就是个最长路,只要建 ...

  6. 洛谷P5284 [十二省联考2019]字符串问题(SAM+倍增+最长路)

    题面 传送门 题解 首先,我们把串反过来,那么前缀就变成后缀,建一个\(SAM\).我们发现一个节点的后缀是它的所有祖先 那么我们是不是直接按着\(parent\)树建边就可以了呢? 显然不是.我们假 ...

  7. 题解 loj3050 「十二省联考 2019」骗分过样例

    CASE \(1\sim 3\) \(n\)组测试数据,每次输入一个数\(x\),求\(19^x\). 测试点\(1\),\(x=0,1,\dots n-1\),可以直接递推. 测试点\(2\)要开l ...

  8. luogu P5291 [十二省联考2019]希望

    luogu loj 无论最终结果将人类历史导向何处 \(\quad\)我们选择 \(\quad\quad\)\(\large{希望}\) 诶我跟你讲,这题超修咸的 下面称离连通块内每个点距离不超过\( ...

  9. 【题解】Luogu P5291 [十二省联考2019]希望

    ytq鸽鸽出的题真是毒瘤 原题传送门 题目大意: 有一棵有\(n\)个点的树,求有多少方案选\(k\)个联通块使得存在一个中心点\(p\),所有\(k\)个联通块中所有点到\(p\)的距离都\(\le ...

随机推荐

  1. <Spark><Programming><RDDs>

    Introduction to Core Spark Concepts driver program: 在集群上启动一系列的并行操作 包含应用的main函数,定义集群上的分布式数据集,操作数据集 通过 ...

  2. synchronized(二)

    package com.bjsxt.base.sync002;/** * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁, * 所以代码中哪个线程先执行synchro ...

  3. scrapy面试一

    1.动态加载又对及时性要求很高怎么处理? Selenium+Phantomjs 尽量不使用 sleep 而使用 WebDriverWait 2.分布式爬虫主要解决什么问题? (1)ip (2)带宽 ( ...

  4. python Django rest-framework 序列化步骤

    django-rest-framework,是一套基于Django 的 REST 框架,是一个强大灵活的构建 Web API 的工具包.本文介绍一下 django-rest-framework 的简单 ...

  5. 性能测试-2.Fiddler抓包工具的使用

    Fiddler基础知识(此文原文地址) Fiddler是强大的抓包工具,它的原理是以web代理服务器的形式进行工作的,使用的代理地址是:127.0.0.1,端口默认为8888,我们也可以通过设置进行修 ...

  6. ES6 Module的语法

    下面比较一下默认输出和正常输出. // 第一组 export default function crc32() { // 输出 // ... } import crc32 from 'crc32'; ...

  7. 2.3 xpath定位

    2.3 xpath定位 前言    在上一篇简单的介绍了用工具查看目标元素的xpath地址,工具查看比较死板,不够灵活,有时候直接复制粘贴会定位不到.这个时候就需要自己手动的去写xpath了,这一篇详 ...

  8. 普通new和placement new的重载

    对于自定义对象,我们可以重载普通new操作符,这时候使用new Test()时就会调用到我们重载的普通new操作符. 示例程序: #include <iostream> #include ...

  9. 【计算机视觉】seetaFace

    class impl class FaceDetection::Impl { public: Impl() : detector_(new seeta::fd::FuStDetector()), sl ...

  10. Java中的初始化顺序

    一.在创建类时为成员变量赋值和在构造函数中的赋值的先后顺序  在未用构造器之前其实已经将类的字段进行了赋值只是在调用构造器时,又将类的字段进行了重新的赋值.如下: package com.cjm.in ...