2021.07.21【NOIP提高B组】模拟 总结
第一题:直接模拟枚举 0 0 0到 9 9 9。
第二题:模板树链剖分。
第三题:首先对于一个点的两棵子树中至少有两个点作为叶子。那么对于两棵子树我们设 f x f_{x} fx表示以 x x x作为最后一个叶子的答案(注意这不是最终答案)。那么就可以去上一棵子树找一个点 y y y转移过来,每一次枚举这两个点的最近公共祖先,用前缀和优化一下,可以证明时间复杂度是 O ( n ) O(n) O(n)。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,a[100005],sz[100005],fa[100005],rnk[100005],dfn[100005],tot;
int la[100005],to[100005],ne[100005],cnt;
int la2[100005],to2[100005],ne2[100005],cnt2;
int cur[100005],f[100005],s1=inf,s2=0;
int ri[100005],le[100005],maxri[100005],maxle[100005],len1,len2;
int pr[100005],sf[100005];
void add(int x,int y){
++cnt;
to[cnt]=y;
ne[cnt]=la[x];
la[x]=cnt;
}
void add2(int x,int y){
++cnt2;
to2[cnt2]=y;
ne2[cnt2]=la2[x];
la2[x]=cnt2;
}
void swap(int &x,int &y){
int t=x;
x=y;
y=t;
}
int max(int x,int y){
return x>y?x:y;
}
int min(int x,int y){
return x<y?x:y;
}
void dfs(int x){
dfn[x]=++tot;
rnk[tot]=x;
sz[x]=1;
for (int i=la[x];i;i=ne[i]){
int y=to[i];
dfs(y);
sz[x]+=sz[y];
}
}
void prer(int x){
ri[++len1]=x;
if (sz[x]==1)return;
prer(to2[la2[x]]);
}
void prel(int x){
le[++len2]=x;
if (sz[x]==1)return;
prel(to[la[x]]);
}
void dfs2(int x){//ri:len1,le:len2
for (int i1=la[x];i1;i1=ne[i1]){
dfs2(to[i1]);
len1=len2=0;
int y=to[i1];
int z=to[ne[i1]];
if (!z)continue;
prer(y);
prel(z);
maxri[0]=maxle[0]=a[x];
for (int i=1;i<=len1;i++)maxri[i]=max(maxri[i-1],a[ri[i]]);
for (int i=1;i<=len2;i++)maxle[i]=max(maxle[i-1],a[le[i]]);
for (int i=len1-1;i>=0;i--)maxri[i+1]=maxri[i];
for (int i=len2-1;i>=0;i--)maxle[i+1]=maxle[i];
pr[0]=-inf;
for (int i=1;i<=len1;i++)pr[i]=max(pr[i-1],f[ri[i]]);
sf[len1+1]=-inf;
for (int i=len1;i;i--)sf[i]=max(sf[i+1],f[ri[i]]-maxri[i]);
int j=1;
for (int i=1;i<=len2;i++){
int ii=le[i];
if (f[ii]!=0)continue;
f[ii]=-inf;
while (maxri[j]<maxle[i]&&j<=len1) j++;
if (j<=len1)f[ii]=max(f[ii],sf[j]+a[ii]);
f[ii]=max(f[ii],pr[j-1]-maxle[i]+a[ii]);
}
}
}
void pre(int x){
f[x]=a[x];
if (sz[x]==1)return;
pre(to[la[x]]);
}
void qry(int x){
s2=max(s2,f[x]);
if (sz[x]==1)return;
qry(to2[la2[x]]);
}
int main(){
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
int len;
scanf("%d",&len);
for (int j=1;j<=len;j++)scanf("%d",&cur[j]),fa[cur[j]]=i,add2(i,cur[j]);
for (int j=len;j;j--)add(i,cur[j]);
}
dfs(1);
pre(1);
dfs2(1);
qry(1);
cout<<s2;
}
第四题:断环成链。设 f i f_i fi表示以 i i i结尾的答案,那么只要在前面找前 3 3 3大的数即可(互不相同),起点枚举 5 5 5个数(互不相同)。