LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想
题目:https://loj.ac/problem/2473
https://www.luogu.org/problemnew/show/P4365
参考:https://blog.csdn.net/xyz32768/article/details/82952313
https://blog.csdn.net/qq_35649707/article/details/79923740
关于如何已知 n+1 个点值 n2 还原出 n 次多项式的系数:
看到连值域也只有 1666 ,首先想到的是枚举第 k 大值是什么,设为 w 。
然后自己只能想到 n6 的 DP ……就是记录 “有 i 个 > w 的值和 j 个 = w 的值” 的连通块个数,求出第 k 大值恰好是 w 的连通块个数。
其实可以这样考虑,就是求 “第 k 大值 >= w ” 的连通块的个数。设为 f[ w ] 的话,答案就是 \( \sum\limits_{w=1}^{W}f[w] \) 。这样对于一个 w 就会被算 w 遍,恰好是答案。
然后就可以枚举 w ,令 \( dp[i][j] \) 表示以 i 为根的子树、有 j 个点的值 >=w 的连通块个数。 j 是和子树大小有关的,所以 DP 是 n3 。
正解是这样考虑:
用整体 DP 的思想,考虑一次 DP 把所有 w 的答案都做出来。
那么 DP 的时候每个点就记录了 n2 个值,来表示有 j 个点的值 >= w 的连通块个数。形如:
转移的时候就是当前点 cr 与孩子 v 的横着的格子对应转移;对于一个横着的格子伸出去的竖着的数组,转移形如卷积的样子。
所以考虑把每个竖着的数组写成一个多项式的样子。\( dp[cr][w]=\sum\limits_{i=0}^{\infty}a_i * x^i \) 这样。
如果把多个 w 像这样同时 DP ,可以发现一个点自己对数组的影响就是:
1.在 w<a[cr] 的那些位置的多项式上 +1,表示多出一个 “有0个点>=w” 的连通块;
2.在 w>=a[cr] 的那些位置的多项式上 *x ,表示原来 “有 j 个点 >=w ” 的连通块会变成 “有 j+1 个点 >=w ” 的连通块。
因为一个点对整个横着的数组的操作很少,所以考虑对横着的数组用动态开点线段树维护。
比如可能经过了好几个点,对 w=3 这个位置和 w=4 这个位置的操作全都是 *x ,那么 w=3 和 w=4 的这两个位置就不用分别维护,像粘在一起的一样打标记就行了。
所以这样可以通过把询问一起做来降低复杂度。
转移就是线段树合并。这样复杂度是 nlogn 。
但是线段树一个节点上维护了一个多项式。要合并很麻烦。所以考虑把 x 换成实际的值,这样线段树的节点上就只记录了一个值,合并的时候就是 O(1) 了。
把 x 换成实际的值求出来的结果是多项式的点值。所以做 n+1 遍求出 n+1 个点值,就能用拉格朗日插值 O(n2) 还原出系数了。
已知原来要求的多项式是 \( \sum\limits_{i=0}^{\infty}a_i * x^i \) ,答案是每个点的线段树所有叶子的多项式中 次数>=k 的那些项的系数和。“所有叶子”表示考虑所有 w 的情况。
那个“每个点”很不好。所以考虑再记一个多项式表示“子树里所有点”的情况,即 \( \sum\limits_{i=0}^{\infty}(\sum\limits_{j \in tree_i}a_{j,i} ) * x^i \) 。
线段树转移的种种就参见参考的那些博客……
注意 unsigned int 类型的所有数都是 >=0 的,也就是它的负数也是 >=0 的。所以 upt( ) 不能写 if(x<0) ... 这样。
自己写(抄)的不知为何常数很大,在洛谷上只能60分,在 LOJ 上可以垫底 AC 。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define u32 unsigned int
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;
}
const int N=,M=N*N<<; u32 mod=;
u32 upt(u32 x){while(x>=mod)x-=mod;return x;}
u32 pw(int x,int k)
{u32 ret=;while(k){if(k&)ret=ret*x%mod;x=x*x%mod;k>>=;}return ret;} int n,m,k,a[N],hd[N],xnt,to[N<<],nxt[N<<];
int rt[N],tot,ls[M],rs[M],dpl[M],dtop;
struct Node{
u32 a,b,c,d;
Node(u32 a=,u32 b=,u32 c=,u32 d=):a(a),b(b),c(c),d(d) {}
Node operator* (const Node &t)const
{
return Node(a*t.a%mod,upt(b*t.a%mod+t.b),
upt(a*t.c%mod+c),upt(b*t.c%mod+t.d+d));
}
void init(){a=;b=c=d=;}
}vl[M];
int nwnd()
{
if(!dtop)return ++tot;
int ret=dpl[dtop--]; vl[ret].init(); return ret;
}
void del(int &x)
{
if(ls[x])del(ls[x]); if(rs[x])del(rs[x]);
dpl[++dtop]=x; x=;
}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void pshd(int cr)
{
if(!ls[cr])ls[cr]=nwnd(); if(!rs[cr])rs[cr]=nwnd();
vl[ls[cr]]=vl[ls[cr]]*vl[cr];
vl[rs[cr]]=vl[rs[cr]]*vl[cr]; vl[cr].init();
}
void mdfy(int l,int r,int &cr,int L,int R,Node k)
{
if(L>R)return; if(!cr)cr=nwnd();
if(l>=L&&r<=R){vl[cr]=vl[cr]*k;return;}
int mid=l+r>>; pshd(cr);
if(L<=mid)mdfy(l,mid,ls[cr],L,R,k);
if(mid<R)mdfy(mid+,r,rs[cr],L,R,k);
}
u32 qry(int l,int r,int cr)
{
if(l==r)return vl[cr].d;
int mid=l+r>>; pshd(cr);
return upt(qry(l,mid,ls[cr])+qry(mid+,r,rs[cr]));
}
void mrg(int &x,int &y)
{
if(!x)swap(x,y);if(!y)return;//
if(!ls[x]&&!rs[x])swap(x,y);
if(!ls[y]&&!rs[y])
{
vl[x]=vl[x]*Node(vl[y].b,,,vl[y].d);
return;
}
pshd(x); pshd(y);/////
mrg(ls[x],ls[y]); mrg(rs[x],rs[y]);
}
void dfs(int cr,int fa,int x)
{
mdfy(,m,rt[cr],,m,Node(,,,));//f=1//m not n!
//but g isn't changed so del()
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs(v,cr,x);
mrg(rt[cr],rt[v]);
del(rt[v]);
}
mdfy(,m,rt[cr],,a[cr],Node(x,,,));
mdfy(,m,rt[cr],,m,Node(,,,)*Node(,,,));
}
int c[N],f[N],g[N],inv[N],ans[N];
void solve()
{
for(int x=;x<=n+;x++)
{
dfs(,,x);
c[x]=qry(,m,rt[]);//m not n!!
del(rt[]);///when dfs del(rt[v])//time?n^2
}
f[]=mod-; f[]=;
for(int i=;i<=n+;i++)
{
for(int j=n+;j;j--)
f[j]=upt((mod-i)*f[j]%mod+f[j-]);
f[]=(mod-i)*f[]%mod;
}
inv[]=;
for(int i=;i<=n+;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
u32 ans=;
for(int i=;i<=n+;i++)
{
g[]=(mod-f[])*inv[i]%mod;
for(int j=;j<=n;j++)
g[j]=upt(g[j-]-f[j]+mod)*inv[i]%mod;
u32 pl=;
for(int j=k;j<=n;j++)pl=upt(pl+g[j]);
u32 ml=c[i];
for(int j=;j<=n+;j++)
if(j<i) ml=ml*inv[i-j]%mod;
else if(j>i) ml=ml*(mod-inv[j-i])%mod;
ans=upt(ans+ml*pl%mod);
}
printf("%d\n",ans);
}
int main()
{
n=rdn();k=rdn();m=rdn();
for(int i=;i<=n;i++)a[i]=rdn();
for(int i=,u,v;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
solve(); return ;
}
LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想的更多相关文章
- LOJ #2473. 「九省联考 2018」秘密袭击
#2473. 「九省联考 2018」秘密袭击 链接 分析: 首先枚举一个权值W,计算这个多少个连通块中,第k大的数是这个权值. $f[i][j]$表示到第i个节点,有j个大于W数的连通块的个数.然后背 ...
- Loj #2479. 「九省联考 2018」制胡窜
Loj #2479. 「九省联考 2018」制胡窜 题目描述 对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度. 接着,我们定义 \(S_i\) 表示 \(S\) 中第 ...
- 「九省联考 2018」IIIDX 解题报告
「九省联考 2018」IIIDX 这什么鬼题,送的55分要拿稳,实测有60? 考虑把数值从大到小摆好,每个位置\(i\)维护一个\(f_i\),表示\(i\)左边比它大的(包括自己)还有几个数可以选 ...
- 【LOJ】#2479. 「九省联考 2018」制胡窜
题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...
- LOJ#2471「九省联考 2018」一双木棋 MinMax博弈+记搜
题面 戳这里 题解 因为每行取的数的个数是单调不增的,感觉状态数不会很多? 怒而记搜,结果过了... #include<bits/stdc++.h> #define For(i,x,y) ...
- [loj 2478][luogu P4843]「九省联考 2018」林克卡特树
传送门 Description 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一 ...
- @loj - 2478@「九省联考 2018」林克卡特树
目录 @description@ @solution@ @part - 1@ @part - 2@ @accepted code@ @details@ @description@ 小 L 最近沉迷于塞 ...
- loj2472 「九省联考 2018」IIIDX
ref #include <algorithm> #include <iostream> #include <cstdio> using namespace std ...
- 洛谷 P4363 [九省联考2018]一双木棋chess 解题报告
P4363 [九省联考2018]一双木棋chess 题目描述 菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落 ...
随机推荐
- Kaggle(2):验证和过分拟合
目前看来,随机森林是比较常用且有效的分类算法.以下先简要介绍python中随机森林的使用方法,然后谈谈分类结果的准确度验证和过拟合等两个重要问题. 1.随机森林的参数 在Scikit learn中使用 ...
- this是什么!
this 1.js的关键字指定一个对象,然后去替代他 函数内的this 函数外的this 函数内的this指向行为发生的主体 函数外的this都指向window 2.函数内的this和函数在什么 ...
- HDU 4463 Outlets(最小生成树给坐标)
Problem Description In China, foreign brand commodities are often much more expensive than abroad. T ...
- react native 之 Android物理返回键
基本用法 根据文档,安卓back键的处理主要就是一个事件监听: BackAndroid.addEventListener('hardwareBackPress', this.onBackPressed ...
- [LeetCode&Python] Problem 717. 1-bit and 2-bit Characters
We have two special characters. The first character can be represented by one bit 0. The second char ...
- OpenCV 自定义任意区域形状及计算平均值 方差
opencv中有矩形的Rect函数.圆形的circl函数等,那么任意形状怎么取呢?方法1:点乘,将其形状与图像进行点乘,求其形状对应的图像形状:方法2:用findContours函数得对应的形状区域, ...
- 20155219&20155224 《信息安全系统设计基础》实验二 固件程序设计
实验二 固件程序设计-1-MDK 0. 注意不经老师允许不准烧写自己修改的代码 1. 两人(个别三人)一组 2. 参考云班课资源中"信息安全系统实验箱指导书.pdf "第一章,1. ...
- 获取div,表单中的内容
获得内容 - text().html() 以及 val() 三个简单实用的用于 DOM 操作的 jQuery 方法: text() - 设置或返回所选元素的文本内容 html() - 设置或返回所选元 ...
- ccf 201312-04 有趣的数(组合数学)
问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次. 2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前. 3. 最高 ...
- npm常规命令行集合
最近在摸索vue-cli脚手架的安装,中间用到了一些node的npm命令行,进行了一些整理,并且这个会一直搜集整理更新! 1,常规文件操作命令 cd.. 返回当前文 ...