[九省联考2018]秘密袭击coat

研究半天题解啊。。。

全网几乎唯一的官方做法的题解:链接

别的都是暴力。。。。

要是n=3333暴力就完了。

一、问题转化

每个联通块第k大的数,直观统计的话,会枚举每个点作为第k大,看看有多少个连通块。但是这样要点分治。而且在d[x]相同的时候可能算重

所以必须以连通块来统计

考虑在连通块的最浅的点统计

但是第k大不一定是这个点

考虑一个套路:用许多次分别统计,最后实际上凑出答案的想法。

而且本题权值范围和n同阶。

外层枚举每一个i∈[1,w],DP找到所有“包含大于等于w的点有至少k个”的连通块的个数

所有的i的总和就是答案。每个连通块恰好统计了第k大的数权值的次数

这样就可以在连通块最浅点统计辣。

二、DP

DP就很显然了

外层枚举i∈[1,w],dp[x][j]以x为根的子树,选择了大于等于i的有j个的方案数

卷积型背包转移。注意每个儿子还可以不选。

最后答案是每次i的每个x的dp[x][k~n]的和的和

看上去似乎优化到头了。(这时候暴力就可以AC了)

但是,

下面开始神仙思路了:

“DP数组是死的,但是套上高级算法就活了”

既然是一个卷积,不妨考虑用生成函数的思想,再用一些多项式的科技和高级数据结构等等来搞搞事情。

现在dp是一个生成函数,首先有:

(注意,可能x有的时候是树的节点编号,有的时候可能是生成函数里的x变量)

$dp[x]=\Pi (dp[son]+1)*Z$

$Z=x^{[d[x]>=i]}$

是一个多项式,当d[x]>=i的时候,Z是x,否则是0,象征x这个点自己是否能够作为>=i的点。

看上去更差劲了,,先接着往下看

三、整体DP

一个强大的思想,是把许多次DP(比如这里要做w次)一起做,叫做整体DP

一般用线段树合并维护,当前点x的线段树的叶子节点下标y的位置的权值,是第y次dp到x这里的权值

考虑把一次影响贡献到所有的DP中。或者说,我们这次外层枚举x,内层枚举每一个DP

线段树合并的时候,更新一些DP值。

但是,,,

这个题每个x的DP是一个数组,不是一个值,,,对应位置卷积,,线段树合并无能为力。。。

生成函数的卷积太麻烦,我们把它变成点值

最外层(主函数)枚举i=1~N+1,象征要求所有的DP的生成函数在x=i时候的点值的和。(注意是和,因为其实是对于每个x统计连通块的个数)

现在,外层枚举到i,dfs到点cur的线段树中下标为y的叶子的f值的意义是:在第y次DP时候,dp[cur]这个生成函数在x=i时候的点值

现在每个位置不是多项式,而是一个值辣。

而我们还要知道所有位置的点值之和,便于统计最后答案

线段树每个叶子再来一个g:外层枚举到i,dfs到点cur的线段树中下标为y的叶子的g值的意义是:在第y次DP时候,整个子树dp[]这个生成函数在x=i时候的点值之和

四、线段树合并

外层枚举i=1~N+1

先考虑要做什么:

1.x的每个位置先赋值为1

2.dfs下去,合并

3.乘上自己的贡献。前[1,d[x]]乘上i,后[d[x]+1,n]乘上1

4.把自己的f贡献到自己的g

4.有一个:$dp[x]=\Pi (dp[son]+1)*Z$

$dp[son]+1$所以干脆把全局f回溯前全局+1

我们需要用线段树维护对于每个子树的对于每个叶子的变换(维护一会说

(最后在rt统计信息不用pushup,只有pushdown)

线段树合并时候对应位置点值相乘(因为真实的dp[x]生成函数最多n项)

对应位置相乘没法做,但是位置同时乘上一个数可以做。由于区间操作,所以在全区间都是同一个“变换”的时候,直接给x树位置乘上变换即可。

变换是什么?

需要支持:

1.维护f,维护g

2.给f+1,给f乘一个数

3.f加到g上去。

设置一个变换(应该是一个群):(a,b,c,d)

定义乘法:$(a1,b1,c1,d1) × (a2,b2,c2,d2)=(a1×a2,a2×b1+b2,c1+c2*a1,c2*b1+d2+d1)$

定义单位元:

$(1,0,0,0)$

推一推发现,这个变换有结合律。可以支持标记合并。

我们把f放在b位置,g放在d位置,就可以维护了。(暴力讨论维护一定也可以,但是绝对不如这个变换的设计好写)

详见代码。

五、插值部分

我们得到的是点值。ans[i]表示最终的生成函数(把所有点的W次求的生成函数加起来)在x=i时候的点值。

用拉格朗日插值还原,见另一篇博客。[学习笔记]拉格朗日插值

六、实现细节:

1.64123相乘爆int,用unsigned int

2.每次可以垃圾回收,所以线段树合并用&,使得y的儿子直接变掉(看代码

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define ui unsigned int
#define mid ((l+r)>>1)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=;
const int mod=;
int n,k,w;
int d[N];
struct node{
int nxt,to;
}e[*N];
int hd[N],cnt;
void add(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
}
struct data{
int a,b,c,d;
data(){}
data(int aa,int bb,int cc,int dd){
a=aa;b=bb;c=cc;d=dd;
}
data friend operator *(data A,data B){
return data((ui)A.a*B.a%mod,((ui)A.b*B.a+B.b)%mod,((ui)B.c*A.a+A.c)%mod,((ui)B.c*A.b+A.d+B.d)%mod);
}
void clear(){
a=,b=,c=,d=;
}
};
struct tr{
int ls,rs;
data val;
tr(){val.a=;val.b=;val.c=;val.d=;ls=rs=;}
void clear(){val.a=;val.b=;val.c=;val.d=;ls=rs=;}
}t[*N];
int sta[*N],top,tot;
int nc(){
return top?sta[top--]:++tot;
}
int rt[N];
int ans[N];//dian zhi
int b[N];//true xishu
int f[N];
int c[N];
int inv[mod+];
void del(int &x){
if(!x) return;
if(t[x].ls) del(t[x].ls);
if(t[x].rs) del(t[x].rs);
sta[++top]=x;
t[x].clear();
x=;
}
void pushdown(int x){
if(!t[x].ls) t[x].ls=nc();
if(!t[x].rs) t[x].rs=nc();
t[t[x].ls].val=t[t[x].ls].val*t[x].val;
t[t[x].rs].val=t[t[x].rs].val*t[x].val;
t[x].val.clear();
}
int query(int x,int l,int r){
if(l==r) return t[x].val.d;
pushdown(x);
int ret=query(t[x].ls,l,mid);
ret=(ret+query(t[x].rs,mid+,r))%mod;
return ret;
}
void upda(int &x,int l,int r,int L,int R,data c){
if(!x) x=nc();
if(L<=l&&r<=R){
t[x].val=t[x].val*c;
return;
}
pushdown(x);
if(L<=mid) upda(t[x].ls,l,mid,L,R,c);
if(mid<R) upda(t[x].rs,mid+,r,L,R,c);
}
int merge(int &x,int &y){
//if(!x||!y) return x+y;每次都会pushdown产生儿子。所以这里不需要
if(!t[x].ls&&!t[x].rs) swap(x,y);
if(!t[y].ls&&!t[y].rs){
t[x].val=t[x].val*data(t[y].val.b,,,);
t[x].val=t[x].val*data(,,,t[y].val.d);
return x;
}
pushdown(x);pushdown(y);
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
return x;
}
void dfs(int x,int fa,int k0){
upda(rt[x],,w,,w,data(,,,));
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
dfs(y,x,k0);
rt[x]=merge(rt[x],rt[y]);
del(rt[y]);
}
upda(rt[x],,w,,d[x],data(k0,,,));
upda(rt[x],,w,,w,data(,,,));
upda(rt[x],,w,,w,data(,,,));
}
void Lagelangri(){
f[]=;
for(reg i=;i<=n+;++i){
for(reg j=n+;j>=;--j){
f[j]=((ui)f[j]*(mod-i)%mod+(j?f[j-]:))%mod;
}
} for(reg i=;i<=n+;++i){
ll tmp=;
for(reg j=;j<=n+;++j){
c[j]=f[j];
if(j!=i) {
if(j<i) tmp=tmp*inv[i-j]%mod;
else tmp=tmp*(mod-inv[j-i])%mod;
}
}
c[]=f[];
tmp=tmp*ans[i]%mod;
for(reg j=;j<=n+;++j){
if(!j) c[j]=(mod-(ll)c[j]*inv[i]%mod)%mod;
else c[j]=(mod-((ll)c[j]-c[j-]+mod)*inv[i]%mod)%mod;
} for(reg j=;j<=n;++j){
b[j]=((ll)b[j]+c[j]*tmp%mod)%mod;
}
}
}
int main(){
rd(n);rd(k);rd(w);
int x,y;
for(reg i=;i<=n;++i){
rd(d[i]);
}
for(reg i=;i<n;++i){
rd(x);rd(y);add(x,y);add(y,x);
}
for(reg i=;i<=n+;++i){
dfs(,,i);
ans[i]=query(rt[],,w);
del(rt[]);
}
inv[]=;
for(reg i=;i<=n+;++i) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
Lagelangri();
ll op=;
for(reg i=k;i<=n;++i){
op=(op+b[i])%mod;
}
printf("%lld",op);
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/2/19 9:45:33
*/

总结:

1.题意转化使得连通块可以直接在最浅的地方统计,模型简单,为后面套高级算法留下可能性

2.生成函数思想+点值,妈妈再也不用担心卷积太复杂!~

3.整体DP,赋值之间大同小异,所以所有DP值一起处理,

4.拉格朗日插值O(n^2)trick

神仙啊~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[九省联考2018]秘密袭击coat的更多相关文章

  1. P4365 [九省联考2018]秘密袭击coat

    $ \color{#0066ff}{ 题目描述 }$ Access Globe 最近正在玩一款战略游戏.在游戏中,他操控的角色是一名C 国士 兵.他的任务就是服从指挥官的指令参加战斗,并在战斗中取胜. ...

  2. 解题:九省联考2018 秘密袭击CoaT

    题面 按照*Miracle*的话来说,网上又多了一篇n^3暴力的题解 可能是因为很多猫题虽然很好,但是写正解性价比比较低? 直接做不可做,转化为统计贡献:$O(n)$枚举每个权值,直接统计第k大大于等 ...

  3. [九省联考 2018]秘密袭击coat

    Description 题库链接 给出一棵 \(n\) 个点的树,每个点有点权.求所有联通块的权值 \(k\) 大和,对 \(64123\) 取模. \(1\leq n,k\leq 1666\) So ...

  4. [LOJ #2473] [九省联考2018] 秘密袭击coat

    题目链接 洛谷. LOJ,LOJ机子是真的快 Solution 我直接上暴力了...\(O(n^2k)\)洛谷要\(O2\)才能过...loj平均单点一秒... 直接枚举每个点为第\(k\)大的点,然 ...

  5. luogu P4365 [九省联考2018]秘密袭击coat

    luogu 这里不妨考虑每个点的贡献,即求出每个点在多少个联通块中为第\(k\)大的(这里权值相同的可以按任意顺序排大小),然后答案为所有点权值\(*\)上面求的东西之和 把比这个点大的点看成\(1\ ...

  6. 【BZOJ5250】[九省联考2018]秘密袭击(动态规划)

    [BZOJ5250][九省联考2018]秘密袭击(动态规划) 题面 BZOJ 洛谷 给定一棵树,求其所有联通块的权值第\(k\)大的和. 题解 整个\(O(nk(n-k))\)的暴力剪剪枝就给过了.. ...

  7. [BZOJ5250][九省联考2018]秘密袭击(DP)

    5250: [2018多省省队联测]秘密袭击 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 3  Solved: 0[Submit][Status][D ...

  8. LuoguP4365 [九省联考2018]秘密袭击

    https://zybuluo.com/ysner/note/1141136 题面 求一颗大小为\(n\)的树取联通块的所有方案中,第\(k\)个数之和. \(n\leq1,667,k\leq n\) ...

  9. 并不对劲的复健训练-bzoj5250:loj2473:p4365:[九省联考2018]秘密袭击

    题目大意 有一棵\(n\)(\(n\leq 1666\))个点的树,有点权\(d_i\),点权最大值为\(w\)(\(w\leq 1666\)).给出\(k\)(\(k\leq n\)),定义一个选择 ...

随机推荐

  1. linux快捷进入长目录的方法

    快捷命令   :alias chaoyang ='cd /usr/lib/uu/hh/lib' 1.将上述设置写到家目录的.bashrc文件中(~/.bashrc) 2.然后source ./bash ...

  2. JVM远程调试功能

    有时候想调试线上的程序 可以启用远程调试功能 在本地调试远程代码. 远程JVM启用调试模式 /usr/local/jdk/bin/java -server -Xms256m -Xmx256m -XX: ...

  3. C#基础第五天

    public struct Person { public string _name; public Gender _sex; public int _age; } public enum Gende ...

  4. win10 系统 wifi自动断开连接 wifi热点不稳定

    我的系统的电脑是win10系统,笔记本 下载了一个wifi共享大师,但是wifi总是自动断,于是就找了找问题所在 在网上看了许多方案,大多数都是    在   电源管理  把[允许计算机关闭此设备以节 ...

  5. 复制命令(ROBOCOPY)

    ROBOCOPY 命令: // 描述: 相比较 xcopy.copy 来说,复制的功能就强大很多,  xcopy.copy 是单线程的,robocopy是多线程的,但是和一些专业的复制软件相比速度还是 ...

  6. 批处理基础知识-IF

    本文主要介绍批处理IF命令的使用. IF命令格式: if /i string=string command 释义:判断2个字符串是否相等,但不区分大小写. 例: 代码: @echo off if /i ...

  7. LeetCode算法题-Assign Cookies(Java实现)

    这是悦乐书的第234次更新,第247篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第101题(顺位题号是455).假设你是一个很棒的父母,并想给你的孩子一些饼干.但是,你 ...

  8. sizeof和strlen()区别及用法

    //sizeof是以字节为单位计算变量或类型所占内存大小,它是属于C语言运算符系列:而strlen()是一个函数,是计算字符串长度(也是以字节为单位,但略有区别):比如: char array[] = ...

  9. Linux经常用到的命令以及快捷键

    Linux常用命令和快捷键 最近一直在对CentOS系统进行各种体验,为方便自己也方便他人,整理了Linux常用命令及快捷键,不过其实大多和DOS是一样的,只是命令的表达上可能有点儿不一样. Linu ...

  10. 初步了解.net

    一..net和C#是什么关系 .net是一个程序运行的平台,它是c#,vb,F#等程序运行的平台,为这些语言提供基础类库.公共语言运行时(CLR)等相关支持. C#是支持.net的一种编程语言..ne ...