2021.9.18考试总结[NOIP模拟56]
T1 爆零
贪心地想,肯定要先走完整个子树再走下一个,且要尽量晚地走深度大的叶子。所以对每个点的儿子以子树树高为关键字排序$DFS$即可。
也可$DP$。
$code:$
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int &x,int &y){ x^=y^=x^=y; }
inline void chmax(int &x,int y){ x=x<y?y:x; }
inline void chmin(int &x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=1e6+5;
int n,pos,ans,mx[NN];
vector<int>to[NN];
bool cmp(int x,int y){ return mx[x]<mx[y]; }
namespace tree_chain{
int siz[NN],son[NN],dep[NN],fa[NN],top[NN];
void dfs1(int s,int f){
fa[s]=f; siz[s]=1;
for(auto v:to[s]) if(v!=f){
dep[v]=dep[s]+1;
dfs1(v,s);
siz[s]+=siz[v];
if(siz[son[s]]<siz[v]) son[s]=v;
}
}
void dfs2(int s,int t){
top[s]=t;
if(!son[s]) return;
dfs2(son[s],t);
for(auto v:to[s])
if(v!=fa[s]&&v!=son[s]) dfs2(v,v);
}
inline int LCA(int x,int y){
while(top[x]!=top[y])
if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
} using namespace tree_chain;
void dfs3(int s){
mx[s]=dep[s];
for(auto v:to[s]) if(v!=fa[s]){
dfs3(v);
chmax(mx[s],mx[v]);
}
}
inline void calc(int p){
ans+=min(dep[pos]+dep[p]-2*dep[LCA(pos,p)],dep[p]);
// cout<<p<<' '<<pos<<' '<<min(dep[pos]+dep[p]-2*dep[LCA(pos,p)],dep[p])<<endl;
pos=p;
}
void dfs(int s){
if(!son[s]) calc(s);
for(auto v:to[s])
if(v!=fa[s]) dfs(v);
}
signed main(){
FILE *A=freopen("a.in","r",stdin);
FILE *B=freopen("a.out","w",stdout);
n=read(); pos=1;
for(int a,i=2;i<=n;i++)
a=read(), to[a].push_back(i);
dfs1(1,0); dfs2(1,1); dfs3(1);
for(int i=1;i<=n;i++) sort(to[i].begin(),to[i].end(),cmp);
// for(int i=1;i<=n;i++){cout<<i<<": ";for(auto v:to[i])cout<<v<<",";cout<<endl;}
dfs(1);
write(ans,'\n');
return 0;
}
T2 底垫
用$set$(柯朵莉树)维护区间并记录区间内数字最后出现在哪个“梦境”内,然后对询问按右端点排序,每次将“梦境”扫描到右端点位置,相当于单调指针,一共扫了一遍。
每次加入新区间时会覆盖一些区间,先考虑如果询问只是$[L,R]$区间并的总长应怎么做。可以开一个树装数组,记录当前右端点情况下每个左端点的答案。
在覆盖区间时,如果把长度为$k$,最后出现位置为$t$的区间覆盖掉了,那么就在树状数组中把$[t+1,i]$的答案加上$k$,因为它的出现位置更新后有更多的左端点会被这个区间贡献。
再考虑这道题,首先对一个$[L,R]$区间,选择的方案数是一定的,为$\begin{pmatrix}R-L+2\\ 2\end{pmatrix}$。接下来仍考虑覆盖区间对答案的贡献。
仍设覆盖区间长度为$k$,最后出现位置为$t$,另设覆盖后区间出现位置为$i$,也就是当前梦境位置。
如果最终询问的$L\in [t+1,i]$,那么它对答案的贡献为$(i-L+1)\times(r-i+1)\times k$;否则贡献为$(i-t)\times(r-i+1)\times k$。
于是可以建四个求后缀和的树状数组,分别记$lr,l,r$的系数和常数,在$i$处与$t$处修改即可。
$code:$
T2
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void chmax(int& x,int y){ x=x<y?y:x; }
inline void chmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=2e5+5,p=1e9+7,inv2=5e8+4;
int n,m,tmp,l[NN],r[NN],ans[NN];
struct question{
int l,r,id;
bool operator<(const question& a)const{
return r<a.r;
}
}q[NN];
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
struct BIT{
int c[NN];
inline void insert(int pos,int x){
while(pos){
(c[pos]+=p+x)%=p;
pos-=pos&-pos;
}
}
inline int query(int pos){
int res=0;
while(pos<=n){
(res+=c[pos])%=p;
pos+=pos&-pos;
}
return res;
}
}ll,rr,lr,cc;
inline void solve(int i,int t,int k){
lr.insert(i,p-k); ll.insert(i,k*(i-1)%p); rr.insert(i,k*(i+1)%p); cc.insert(i,(p+k-k*i%p*i%p)%p);
lr.insert(t,k); ll.insert(t,k*(p+1-i)%p); rr.insert(t,k*(p-i-1)%p); cc.insert(t,(p-k+k*i%p*i%p)%p);
rr.insert(t,k*(i-t)%p); cc.insert(t,k*(p+i+t*i%p-t-i*i%p)%p);
}
namespace ODT{
#define sit set<node>::iterator
struct node{
int l,r;
mutable int val;
node(){}
node(int a,int b,int c){ l=a; r=b; val=c; }
node(int a){ l=a; }
bool operator<(const node& a)const{
return l<a.l;
}
};
set<node>s;
inline sit split(int pos){
sit it=s.lower_bound(node(pos));
if(it!=s.end()&&it->l==pos) return it;
--it;
int l=it->l,r=it->r,val=it->val;
s.erase(it); s.insert(node(l,pos-1,val));
return s.insert(node(pos,r,val)).first;
}
inline void assign(int l,int r,int val){
sit itr=split(r+1),itl=split(l);
for(sit it=itl;it!=itr;++it){
int ll=it->l,k=it->r-it->l+1;
solve(val,it->val,k);
}
s.erase(itl,itr);
s.insert(node(l,r,val));
}
} using namespace ODT;
signed main(){
FILE *A=freopen("b.in","r",stdin);
FILE *B=freopen("b.out","w",stdout);
n=read(); m=read(); s.insert(node(0,(int)1e9+5,0));
for(int i=1;i<=n;i++) l[i]=read(), r[i]=read()-1;
for(int i=1;i<=m;i++) q[i].l=read(), q[i].r=read(), q[i].id=i;
sort(q+1,q+m+1); tmp=1;
for(int i=1;i<=m;i++){
for(;tmp<=q[i].r;++tmp) assign(l[tmp],r[tmp],tmp);
ans[q[i].id]=q[i].l*q[i].r%p*lr.query(q[i].l)%p+q[i].l*ll.query(q[i].l)%p+q[i].r*rr.query(q[i].l)+cc.query(q[i].l);
ans[q[i].id]%=p;
(ans[q[i].id]*=qpow((q[i].r-q[i].l+1)*(q[i].r-q[i].l+2)%p*inv2%p,p-2))%=p;
}
for(int i=1;i<=m;i++) write(ans[i],'\n');
return 0;
}
T3高考
首先可以发现最后所有局面出现概率相等。
$\frac{\prod(a_i-1)!}{n(n+1)\dots (n+m-1)}\times\frac{m!}{\prod(a_i-1)!}=\frac{1}{\begin{pmatrix}n+m-1\\n-1\end{pmatrix}}$
所以期望题右变成了计数题。考虑设$f_{i,j}$为至少$i$个数字大于等于$j$的方案数,考虑贡献次数,其实对于$r$,答案即为$\sum_{i=1}^r\sum_{j=1}^mf_{i,j}$。
再设$g_{i,j}$为恰有$i$个数字大于等于$j$的方案数,那么$f$即为$g$的后缀和。对于$g$可通过二项式反演求。
$g_{i,j}=\sum_{k=i}^n\begin{pmatrix}k\\ i\end{pmatrix}(-1)^{k-i}\begin{pmatrix}n\\ k\end{pmatrix}\begin{pmatrix}m+n-1-jk\\ n-1\end{pmatrix}$
右边两个组合数可通过划分数的方案来理解。
这样求出的方案默认开始时所有数为$0$,因此每个答案最后要再加$r$。如果要从$1$开始,需要改一改上界和求$g$的式子。
我打的是从$1$开始。
$code:$
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int &x,int &y){ x^=y^=x^=y; }
inline void chmax(int &x,int y){ x=x<y?y:x; }
inline void chmin(int &x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=5005,p=1e9+7,MX=10010;
int n,m,invv,f[NN][NN],g[NN][NN],fac[MX+1],inv[MX+1];
inline int C(int x,int y){ return x<y?0:fac[x]*inv[y]%p*inv[x-y]%p; }
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
signed main(){
FILE *A=freopen("c.in","r",stdin);
FILE *B=freopen("c.out","w",stdout);
n=read(); m=read(); fac[0]=inv[0]=1;
for(int i=1;i<=MX;i++) fac[i]=fac[i-1]*i%p; inv[MX]=qpow(fac[MX],p-2);
for(int i=MX-1;i;i--) inv[i]=inv[i+1]*(i+1)%p;
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1&&i*j<=m+n;j++)
for(int k=i;k<=n&&j*k<=m+n;k++){
int tmp=C(n,k)*C(k,i)%p*C(m+n-1-j*k+k,n-1)%p;
(g[i][j]+=(k-i)&1?p-tmp:tmp)%=p;
}
for(int i=n;i;i--) for(int j=1;j<=m+1;j++){
f[i][j]=(f[i+1][j]+g[i][j])%p;
(f[i][0]+=f[i][j])%=p;
}
invv=qpow(C(n+m-1,n-1),p-2);
for(int i=1;i<=n;i++){
(f[i][0]+=f[i-1][0])%=p;
write((f[i][0])*invv%p,'\n');
}
return 0;
}
T4 种田
枚举$\frac{S}{T}$有$70$,精心$rand$有$100$,无正解。
2021.9.18考试总结[NOIP模拟56]的更多相关文章
- 2021.10.18考试总结[NOIP模拟76]
T1 洛希极限 不难发现每个点肯定是被它上一行或上一列的点转移.可以预处理出每个点上一行,上一列最远的能转移到它的点,然后单调队列优化. 预处理稍显ex.可以用并查集维护一个链表,记录当前点之后第一个 ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- 2021.9.13考试总结[NOIP模拟52]
T1 路径 考虑每一位的贡献,第$i$位每$2^i$个数会变一次,那么答案为$\sum_{i=1}^{log_2n} \frac{n}{2^i}$. $code:$ 1 #include<bit ...
- 8.18考试总结[NOIP模拟43]
又挂了$80$ 好气哦,但要保持优雅.(草 T1 地衣体 小小的贪心:每次肯定从深度较小的点向深度较大的点转移更优. 模拟一下,把边按链接点的子树最大深度排序,发现实际上只有上一个遍历到的点是对当前考 ...
- 2021.8.11考试总结[NOIP模拟36]
T1 Dove玩扑克 考场并查集加树状数组加桶期望$65pts$实际$80pts$,考后多开个数组记哪些数出现过,只扫出现过的数就切了.用$set$维护可以把被删没的数去掉,更快. $code:$ 1 ...
- 2021.7.29考试总结[NOIP模拟27]
T1 牛半仙的妹子图 做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数. 我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm ...
- 2021.7.15考试总结[NOIP模拟16]
ZJ模拟D2就是NB.. T1 Star Way To Heaven 谁能想到这竟是个最小生成树呢?(T1挂分100的高人JYF就在我身边 把上边界和下边界看成一个点和星星跑最小生成树,从上边界开始跑 ...
- 2021.9.14考试总结[NOIP模拟53]
T1 ZYB和售货机 容易发现把每个物品都买成$1$是没有影响的. 然后考虑最后一个物品的方案,如果从$f_i$向$i$连边,发现每个点有一个出度多个入度,可以先默认每个物品都能买且最大获利,这样可以 ...
- 2021.9.12考试总结[NOIP模拟51]
T1 茅山道术 仔细观察发现对于每个点只考虑它前面第一个与它颜色相同的点即可. 又仔细观察发现对一段区间染色后以这个区间内点为端点的区间不能染色. 于是对区间右端点而言,区间染色的贡献为遍历到区间左端 ...
随机推荐
- 安全系列之:跨域资源共享CORS
目录 简介 CORS举例 CORS protocol HTTP request headers HTTP response headers 基本CORS Preflighted requests 带认 ...
- uniapp 获取用户手机号
参考资料: 微信小程序官方文档 uniapp开发微信小程序获取用户手机号 页面增加一个按钮 <button open-type="getPhoneNumber" @getph ...
- 【多线程】Android多线程学习笔记——线程池
Java线程池采用了享元设计模式,在系统中维持一定数量的线程,用于处理异步或并发需求,在平时处理异步或并发任务时被广泛使用.这里基于JDK1.8和Android28来整理一些关于线程池的知识点. 一. ...
- nginx 禁止某IP访问
首先建立下面的配置文件放在nginx的conf目录下面,命名为blocksip.conf: deny 95.105.25.181; 保存一下. 在nginx的配置文件nginx.conf中加入:inc ...
- JS HTML5仿微信朋友圈特效
完美! 图片相册翻页可定位在第几张,右上角可关闭. 源代码下载地址: 链接: https://pan.baidu.com/s/1o7PA7wu 密码: asyt
- str.strip(chars)
strip会去除给定字符串的指定字符,指定字符可以是一个或多个,去除从左右分别进行,没有则忽略,如果需要去除某个中间的字符,必须先去除外围的字符 看几个例子,以s为例,故意设置为非对称结构, s = ...
- 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01
百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...
- CF622F-The Sum of the k-th Powers【拉格朗日插值】
正题 题目链接:https://www.luogu.com.cn/problem/CF622F 题目大意 给出\(n,k\),求 \[\sum_{i=1}^ni^k \] 解题思路 很经典的拉格朗日差 ...
- P6775-[NOI2020]制作菜品【贪心,dp】
正题 题目链接:https://www.luogu.com.cn/problem/P6775 题目大意 \(n\)种原材料,第\(i\)个有\(d_i\)个,\(m\)道菜品都需要\(k\)个原料而且 ...
- RabbitMQ 3.9.7 镜像模式集群的搭建
1. 概述 老话说的好:做人脚踏实地,一步一个脚印,便定能战胜一切困难,最终取得成功!!! 言归正传,之前我们聊了 RabbitMQ 单点服务的安装,今天我们来聊聊 RabbitMQ 3.9.7 镜像 ...