麻烦的动态DP写了2天

简化题意:给树,求比给定独立集字典序大k的独立集是哪一个

主要思路:

k排名都是类似二分的按位确定过程。

字典序比较本质是LCP下一位,故枚举LCP,看多出来了多少个独立集,然后判断;枚举完LCP,再往回做过去。

外面:

假如是一串0101010表示每个点有没有在独立集里,然后比较这个01串的字典序?

枚举LCP,LCP下一位:原来是0,这次就是1;原来是1,这次必须还是1。后面的随便选择。找方案数

但是这里是一般的字典序,两个1中间的0都会省略,使得大小比较没有那么容易

但是也有字典序按位比较的方法(2随便选)

写成01序列00001010100001001010000

首先,最后的0000先都变成2222,找方案数,(因为后面再放上一个1,由于长度更长,所以字典序还是大的。如果原来序列后面有1就不行了(也是为什么下面要跳过中间的0))

不够,每次找最后一个1,变成0,后面都是2,找方案数

两个1中间的0都变成2,这些0变成1的话,直接就比原来的字典序小了。

注意,每一次会有一个后面全是00000的,特殊-1

最后如果剩下lcp是0,就S=∅ return 0

否则往回找,开始从(1,0)或者(1,1)后面第一个(0,0)开始找(后面的暂时还是2)

大概就是两种情况都是从红色箭头开始

先填1,(因为1小),如果方案大于等于remain,就把1放这儿,remain-=1(因为后面都填0也是一种方案了)

如果小于remain,减掉这次的方案数,然后放0

当途中remain==0,一定是k从1到0,直接break掉

最后in[x]=1的就是最终独立集了。

动态DP:

统计独立集方案数,f[x][0],f[x][1]表示方案数即可

方案数大于k,和k+2取min

取min了,修改轻儿子不能直接把轻儿子贡献除去,线段树维护轻儿子的f[x][0]+f[x][1]和f[x][0]

每个重链开一个线段树

注意:
1.重链在top建立,直接不断找重儿子即可

2.判乘法>k,会爆long long,用long double会被卡常,所以

return __builtin_clzll(a)+__builtin_clzll(b)<?k+2:a*b;

3.常数优化还可以用const mat &t=...直接引用减少复制常数。

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define mid ((l+r)>>1)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;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=1e6+;
const int M=4e6+;
int n;
int b[N][];
int a[N],I;
int ori[N];
ll k;
int hd[N],cnt;
int bian[N][];
struct node{
int nxt,to;
}e[*N];
void addedge(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
}
ll mul(const ll &a,const ll &b){
if(!a||!b) return ;
if(a>k+||b>k+) return k+;
return __builtin_clzll(a)+__builtin_clzll(b)<?k+:a*b;
}
ll add(const ll &a,const ll &b){
return a+b>k+?k+:a+b;
}
struct mat{
ll a[][];
void clear(){memset(a,,sizeof a);}
void pre(){a[][]=;a[][]=;a[][]=a[][]=;}
void init(){a[][]=a[][]=a[][]=;a[][]=;}
mat operator *(const mat&b){
mat c;
c.a[][]=add(mul(a[][],b.a[][]),mul(a[][],b.a[][]));
c.a[][]=add(mul(a[][],b.a[][]),mul(a[][],b.a[][]));
c.a[][]=add(mul(a[][],b.a[][]),mul(a[][],b.a[][]));
c.a[][]=add(mul(a[][],b.a[][]),mul(a[][],b.a[][]));
return c;
}
void op(){
cout<<a[][]<<" "<<a[][]<<endl;
cout<<a[][]<<" "<<a[][]<<endl;
}
}st[N],base,nowdp; int dfn[N],top[N],dep[N],son[N],sz[N],fa[N];
int fdfn[N];
int low[N];
int df;
int totson[N];
int num[N];//i is i's fa's num[i] son
int in[N];//0 not 1 yes
ll f[N][]; namespace seg1{//get lian
int tot;
int ls[M],rs[M];
mat data[M];
struct tr1{
int ss,nd;
int rt;
void pushup(int x){
data[x]=data[rs[x]]*data[ls[x]];
}
mat get(){
return data[rt];
}
void build(int &x,int l,int r){
x=++tot;
// cout<<" x "<<x<<" "<<l<<" "<<r<<endl;
if(l==r){
data[x]=st[fdfn[l]];return;
}
build(ls[x],l,mid);build(rs[x],mid+,r);
pushup(x);
}
void chan(int p,ll c0,ll c1){
upda(rt,ss,nd,p,c0,c1);
}
mat que(int L,int R){
return query(rt,ss,nd,L,R);
}
mat query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
return data[x];
}
mat ret;
ret.pre();
if(mid<R) ret=ret*query(rs[x],mid+,r,L,R);
if(L<=mid) ret=ret*query(ls[x],l,mid,L,R);
return ret;
}
void upda(int x,int l,int r,int p,ll c0,ll c1){
if(l==r){
data[x].a[][]=c0;
data[x].a[][]=c0;
data[x].a[][]=c1;
return;
}
if(p<=mid) upda(ls[x],l,mid,p,c0,c1);
else upda(rs[x],mid+,r,p,c0,c1);
pushup(x);
}
}; }using seg1::tr1;
tr1 t1[N]; int mem[N];
namespace seg2{//get son
int tot;
int ls[M],rs[M];
ll s0[M],s1[M];
struct tr2{
int sz,rt;
void pushup(int x){
s0[x]=mul(s0[ls[x]],s0[rs[x]]);
s1[x]=mul(s1[ls[x]],s1[rs[x]]);
}
void build(int &x,int l,int r){
x=++tot;
if(l==r){
s0[x]=f[mem[l]][]+f[mem[l]][];
s1[x]=f[mem[l]][];
return;
}
build(ls[x],l,mid);build(rs[x],mid+,r);
pushup(x);
}
void chan(int p,ll c0,ll c1){
upda(rt,,sz,p,c0,c1);
}
void upda(int x,int l,int r,int p,ll c0,ll c1){
if(l==r){
s0[x]=c0;
s1[x]=c1;
return;
}
if(p<=mid) upda(ls[x],l,mid,p,c0,c1);
else upda(rs[x],mid+,r,p,c0,c1);
pushup(x);
}
void spe(int &x){
x=++tot;
s0[x]=;s1[x]=;
}
ll get0(){
return s0[rt];
}
ll get1(){
return s1[rt];
}
}; }using seg2::tr2;
tr2 t2[N]; int sta[N],tp;
void dfs1(int x,int d){
dep[x]=d;
sz[x]=;
if(!in[x])f[x][]=;
else f[x][]=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x]) continue;
fa[y]=x;
dfs1(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs2(int x){
dfn[x]=++df;
sta[++tp]=x;
fdfn[df]=x;
if(!top[x]) top[x]=x;
if(son[x]) top[son[x]]=top[x],++totson[x],dfs2(son[x]);
st[x].init(); for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==son[x]) continue;
if(y==fa[x]) continue;
num[y]=totson[x];
++totson[x];
dfs2(y);
if(in[x]){
st[x].a[][]*=(f[y][]);
}else{
st[x].a[][]*=(f[y][]+f[y][]);
st[x].a[][]*=(f[y][]+f[y][]);
}
}
if(in[x]) st[x].a[][]=st[x].a[][]=;
else st[x].a[][]=; if(top[x]==x){
t1[x].ss=dfn[x];
t1[x].nd=dfn[sta[tp]];
t1[x].build(t1[x].rt,t1[x].ss,t1[x].nd);
int z;
do{
z=sta[tp];
--tp;
}while(z!=x);
}
if(totson[x]<=){//special
t2[x].spe(t2[x].rt);
}
else if(totson[x]>){
int lp=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
mem[++lp]=y;
}
t2[x].sz=lp;
t2[x].build(t2[x].rt,,lp);
}
}
mat upda(int x){
mat ret;
ret.clear();
while(x){
if(in[x]==){//must not
t1[top[x]].chan(dfn[x],t2[x].get0(),);
}else if(in[x]==){//must yes
t1[top[x]].chan(dfn[x],,t2[x].get1());
}else{//either
t1[top[x]].chan(dfn[x],t2[x].get0(),t2[x].get1());
}
ret=base*t1[top[x]].get();
x=top[x];
if(fa[x]){
t2[fa[x]].chan(num[x],add(ret.a[][],ret.a[][]),ret.a[][]);
}
x=fa[x];
}
return ret;
}
int main(){
rd(n);
scanf("%lld",&k);
base.a[][]=; for(reg i=;i<n;++i) rd(b[i][]),++b[i][];
for(reg i=;i<n;++i) {
rd(b[i][]);
++b[i][];
addedge(b[i][],b[i][]);
addedge(b[i][],b[i][]);
}
rd(I);
for(reg i=;i<=I;++i) rd(a[i]),++a[i],in[a[i]]=,ori[a[i]]=;
sort(a+,a+I+); if(k==){
for(reg i=;i<=I;++i){
printf("%d ",a[i]-);
}
return ;
}
dfs1(,);
dfs2();
//shu lian pou fen int lcp=I;//warning!! numth
ll remain=k;
for(reg i=a[lcp]+;i<=n;++i){
in[i]=;
nowdp=upda(i);
}
ll tot=add(nowdp.a[][],nowdp.a[][]);
int lp=; if(tot-<remain){
remain-=max(1LL*,tot-);
int ptr=a[lcp]-;
for(;lcp;--lcp){
in[a[lcp]]=;
nowdp=upda(a[lcp]);
tot=add(nowdp.a[][],nowdp.a[][]);
if(tot-<remain){
remain-=tot-;
in[a[lcp]]=;
nowdp=upda(a[lcp]);
}else{
break;
}
ptr=a[lcp]-;
while(ptr&&ori[ptr]!=){
in[ptr]=;
nowdp=upda(ptr);
--ptr;
}
}
if(!lcp){
return ;
}
} lp=a[lcp]+;
for(reg i=lp;i<=n;++i){
in[i]=;
nowdp=upda(i);
tot=add(nowdp.a[][],nowdp.a[][]);
if(tot<remain){
remain-=tot;
in[i]=;
nowdp=upda(i);
}else{
--remain;
}
if(remain==) break;
}
for(reg i=;i<=n;++i){
if(in[i]==){
printf("%d ",i-);
}
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/2/20 11:04:52
*/

字典序处理、轻儿子维护值得注意

「LibreOJ NOI Round #1」验题的更多相关文章

  1. 「LibreOJ NOI Round #2」不等关系

    「LibreOJ NOI Round #2」不等关系 解题思路 令 \(F(k)\) 为恰好有 \(k\) 个大于号不满足的答案,\(G(k)\) 表示钦点了 \(k\) 个大于号不满足,剩下随便填的 ...

  2. LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿

    二次联通门 : LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿 /* LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿 dp 记录一下前驱 ...

  3. LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)

    题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...

  4. 「LibreOJ NOI Round #2」单枪匹马

    嘟嘟嘟 这题没卡带一个\(log\)的,那么就很水了. 然后我因为好长时间没写矩阵优化dp,就只敲了一个暴力分--看来复习还是很关键的啊. 这个函数显然是从后往前递推的,那么令第\(i\)位的分子分母 ...

  5. #509. 「LibreOJ NOI Round #1」动态几何问题

    下面给出部分分做法和满分做法 有一些奇妙的方法可以拿到同样多的分数,本蒟蒻只能介绍几种常见的做法 如果您想拿18分左右,需要了解:质因数分解 如果您想拿30分左右,需要了解:一种较快的筛法 如果您想拿 ...

  6. #510. 「LibreOJ NOI Round #1」动态几何问题

    题目: 题解: 几何部分,先证明一下 \(KX = \sqrt{a},YL = \sqrt{b}\) 设左侧的圆心为 \(O\) ,连接 \(OK\) ,我们有 \(OK = r\). 然后有 \(r ...

  7. #507. 「LibreOJ NOI Round #1」接竹竿 dp

    题目: 题解: 我们考虑把每对花色相同的牌看作区间. 那么如果我们设 \(f_i\) 表示决策在 \([1,i]\) 内的最优答案. 那么有 \(f_i = max\{max\{(f_{j-1}+\s ...

  8. LOJ 510: 「LibreOJ NOI Round #1」北校门外的回忆

    题目传送门:LOJ #510. 题意简述: 给出一个在 \(K\) 进制下的树状数组,但是它的实现有问题. 形式化地说,令 \(\mathrm{lowbit}(x)\) 为在 \(K\) 进制下的 \ ...

  9. LOJ575. 「LibreOJ NOI Round #2」不等关系 [容斥,分治FFT]

    LOJ 思路 发现既有大于又有小于比较难办,使用容斥,把大于改成任意减去小于的. 于是最后的串就长成这样:<<?<?<??<<<?<.我们把一段连续的& ...

随机推荐

  1. Delphi连接MySql(待测试验证,使用mysql.pas未通过)

    要在一个Delphi程序中调用Mysql数据库,查到有个资料如下,待验证,验证后会给出结果.暂时做个标记 已经验证,验证日期:2018.6.18 验证结果:不可行 验证工具:XE7,mysql5.5. ...

  2. 挖一挖MongoDB的备份与还原(实现指定时间点还原和增量备份还原)

    一  研究背景需求 目前作者所在公司的MongoDB数据库是每天凌晨做一次全库完整备份,但数据库出现故障时,只能保证恢复到全备时间点,比如,00:30 做的完整备份,而出现故障是下午18:00,那么现 ...

  3. c/c++ 多线程 mutex的理解

    多线程 mutex的理解 mutex,我的理解是每个mutex对象都是一个带锁头的门,这个门有两个状态,门开着和门关着,感觉像是废话... 当想查看门的里东西,或者把东西放进门里,或者从门里拿出东西前 ...

  4. 利用ZYNQ SOC快速打开算法验证通路(2)——数据传输最简方案:网络调试助手+W5500协议栈芯片

    在上一篇该系列博文中讲解了MATLAB待处理数据写入.bin二进制数据文件的过程,接下来需要将数据通过以太网发送到ZYNQ验证平台.之前了解过Xilinx公司面向DSP开发的System Genera ...

  5. SQLServer之修改UNIQUE约束

    使用SSMS数据库管理工具修改UNIQUE约束 1.连接数据库,选择数据表->右键点击->选择设计(或者展开键,选择要修改的键,右键点击,选择修改,后面步骤相同). 2.选择要修改的数据列 ...

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

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

  7. (笔记)CTF入门指南

    [考项分类] Web: 网页安全 Crypto: 密码学(凯撒密码等) PWN: 对程序逻辑分析 系统漏洞利用 Misc: 杂项 图片隐写 数据还原 脑洞类 信息安全有关的 Reverse: 逆向工程 ...

  8. vue2.0阻止事件冒泡

    <!--picker弹窗--><transition name="fade"> <div class="picker_wrap" ...

  9. wangEditor的使用

    wangEditor的使用 第一步,将其下载,并引入项目中. 第二步,引入js <script type="text/javascript" src="/plugi ...

  10. 返回数组中指定的一列,将键值作为元素键名array_column

    array_column() 函数 从记录集中取出 last_name 列: <?php // 表示由数据库返回的可能记录集的数组 $a = array( array( 'id' => 5 ...