过分的神圣,往往比恶魔更加恶质。

前言

最大的一个收获就是不要动不动就码线段树,一定要审清楚题目之后再码!!

T1 一开始理解错题了,以为答案是就是 \(\dfrac{\operatorname{len}(s,t)}{k}\) 然后就傻呵呵地码完了,然后看了看整个机房都没有什么动静,才发现自己的无知。

后来就是换做法,以为是 线段树+树链剖分 ,想了好久的区间合并,最后想的差不多了,就开始码线段树,刚刚码完就发现直接 前缀和+lower_bound 就可以解决这个问题,主要是这个思路还是错的。。。

比较成功的就是 20min 码完了 T3 的小暴力,然后稍微调了两下就过了。。

T1 第零题

解题思路

有一个结论:

对于⼀条链,如果体⼒都是满的从两边开始⾛,那么复活的次数是一样的

证明的话,这里只证明一下比较难的部分:一条全部由 \(<k\) 的价值构成的链。

假设现在有一条总和在 \((k,2k)\) 之间的链,并且满足上述条件,那么加入说我们把起始点向另一端移一条边(保证链长依旧大于 k ),那么最后的复活次数还是 1 。

同样的,扩展到一个总和未知的链上,它也一定可以分成一段 \(<k\) 的和若干的长度 \(\ge k\) 的段,通过上述操作最终的复活次数还是一样的。

那么我们就可以以 LCA 为中心,对于这条链的左右两半部分分别向两端移动,最后计算中间的部分。

然后就可以通过倍增进行维护。

对于一个点记录这个点到根节点上的路径的各个点到根节点的距离以及对应的序号,然后在这个数组上二分就可以得到从这个点满状态开始向上的第一个死亡点了。

倍增数组维护就不用说了吧。。

然后对于起点 s 直接向上倍增跳复活数组,最后得到的就是到 LCA 的距离最大且距离 \(<k\) 的点。

接下来在 t 到 LCA 的那一条链上跳祖先直到到 LCA 的距离与前面求的剩余距离之和 \(\ge k\) 的点就是我们要移动的那一段,判断这一段是否 \(\ge k\) 就好了。

对于 t 到 LCA 链上剩下的点操作和 s 的那条链相差无几。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10;
int n,m,q,top,sta[N],st[N],s[N],f[N][25];
int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1];
int tim,dfn[N],id[N],son[N],siz[N],dep[N],dis[N],topp[N],fa[N];
int die[N][25];
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
edge[tot]=val;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
dis[to]=dis[x]+edge[i];
dep[to]=dep[x]+1;
fa[to]=x; s[to]=edge[i];
f[to][0]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
id[tim]=x;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
void dfs3(int x)
{
if(dis[x]-dis[fa[x]]>=m) die[x][0]=fa[x];
else
{
int pos=upper_bound(sta+1,sta+top+1,dis[x]-m)-sta-1;
if(dis[x]-sta[pos]<m) die[x][0]=0;
else die[x][0]=st[pos];
}
sta[++top]=dis[x]; st[top]=x;
for (int i=0;die[x][i];i++)
die[x][i+1]=die[die[x][i]][i];
for (int i=0;f[x][i];i++)
f[x][i+1]=f[f[x][i]][i];
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
dfs3(to);
}
top--;
}
signed main()
{
n=read(); m=read();
for(int i=1,x,y,val;i<n;i++)
x=read(),y=read(),val=read(),
add_edge(x,y,val),add_edge(y,x,val);
dfs1(1); dfs2(1,1); dfs3(1);
q=read();
while(q--)
{
int x,y,lca,val,temp,ans=0;
x=read(); temp=y=read(); lca=LCA(x,y);
for(int i=20;i>=0;i--)
if(die[x][i]&&dep[die[x][i]]>=dep[lca])
x=die[x][i],ans+=(1ll<<i);
val=dis[x]-dis[lca];
for(int i=20;i>=0;i--)
if(f[temp][i]&&dis[f[temp][i]]-dis[lca]>=m-val)
temp=f[temp][i];
if(dis[temp]+dis[x]-2*dis[lca]>=m) ans++;
for(int i=20;i>=0;i--)
if(die[y][i]&&dep[die[y][i]]>=dep[temp])
y=die[y][i],ans+=(1ll<<i);
printf("%lld\n",ans);
}
return 0;
}

T2 第负一题

解题思路

官方题解又双叒叕不说人话。。。

对于一个区间 \([l,r]\) 我们显然可以运用类似于 没有上司的舞会 的思路 \(\mathcal{O}(r-l+1)\) 求出来。

DP 转移柿子就是 \(f_{i,0}=\max(f_{i-1,0},f_{i-1,1}),f_{i,1}=f_{i-1,0}+s_i\)

对于正解的话,二分

对于每一个二分到的区间分成 \(L,R\) 两个部分,对于每半个区间维护两个值,\(fl_{i,0/1},fr_{i,0/1}\)

\(fl_{i,0}\) 表示默认不选择 \(mid\) 这个元素的在 \([i,mid]\) 的范围内可以得到的最大值。

\(fl_{i,1}\) 表示默认选择 \(mid\) 这个元素的在 \([i,mid]\) 的范围内可以得到的最大值。

\(fr\) 数组的定义类似,只不过范围是 \([mid+1,i]\)

然后对于位于 \(L\) 的 \(i\) 以及位于 \(R\) 的 \(j\),在 \([i,j]\) 区间的最大值就是:

\[\max\{fl_{i,0}+fr_{j,0},fl_{i,1}+fr_{j,0},fl_{i,0}+fr_{j,1}\}
\]

那么我们假设 \(ld_i=\max(0,fl_{i,1}-fl_{i,0}),rd_i=\max(0,fr_{i,1}-fr_{i,0})\)

然后对于区间 \([i,j]\) 的答案就是 \(fl_{i,0}+fr_{j,0}+\max(ld_i,rd_j)\)

每个 \(ld_i\) 以及 \(rd_j\) 的贡献就可以直接通过乘上另一半的区间长度来算。

那么问题就变为了求 \(\sum\limits_{i=l}^{mid}\sum\limits_{j=mid+1}^r \max(ld_i,rd_j)\)

我们先将 \(ld,rd\) 数组从小到大进行排序,扫描 \(L\) 部分,另外用一个指针 \(pos\) 维护 \(R\) 部分。

保证 \([mid+1,pos-1]\) 都是小于 \(ld_i\) 的,由此可以算出每一个 \(ld_i\) 的贡献。

在维护 \(pos\) 指针的同时统计当前的 \(rd_{pos}\) 的贡献,计算方式类似于上面的 \(ld_i\) 。

最后不要忘了把指针一直算到右端点就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10,mod=998244353,INF=1e18;
int n,ans,s[N],fl[N][2],fr[N][2],f[N][2],ld[N],rd[N];
void Binary(int l,int r)
{
if(l==r) return ans=(ans+s[l])%mod,void();
int mid=(l+r)>>1,pos=mid+1;
Binary(l,mid); Binary(mid+1,r); f[mid][0]=-INF; f[mid][1]=s[mid]; fl[mid][1]=s[mid];
for(int i=mid-1;i>=l;i--)
{
f[i][0]=max(f[i+1][0],f[i+1][1]);
f[i][1]=f[i+1][0]+s[i];
fl[i][1]=max(f[i][0],f[i][1]);
} f[mid][0]=0; f[mid][1]=-INF; fl[mid][0]=0;
for(int i=mid-1;i>=l;i--)
{
f[i][0]=max(f[i+1][0],f[i+1][1]);
f[i][1]=f[i+1][0]+s[i];
fl[i][0]=max(f[i][0],f[i][1]);
} for(int i=mid;i>=l;i--)
{
ld[i]=max(0ll,fl[i][1]-fl[i][0]);
ans=(ans+fl[i][0]*(r-mid))%mod;
} f[mid+1][0]=-INF; f[mid+1][1]=s[mid+1]; fr[mid+1][1]=s[mid+1];
for(int i=mid+2;i<=r;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+s[i];
fr[i][1]=max(f[i][1],f[i][0]);
} f[mid+1][0]=0; f[mid+1][1]=-INF; fr[mid+1][0]=0;
for(int i=mid+2;i<=r;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+s[i];
fr[i][0]=max(f[i][1],f[i][0]);
} for(int i=mid+1;i<=r;i++)
{
rd[i]=max(0ll,fr[i][1]-fr[i][0]);
ans=(ans+fr[i][0]*(mid-l+1))%mod;
} sort(ld+l,ld+mid+1); sort(rd+mid+1,rd+r+1);
for(int i=l;i<=mid;i++)
{
while(pos<=r&&rd[pos]<=ld[i]) ans=(ans+rd[pos]*(i-l))%mod,pos++;
ans=(ans+(pos-mid-1)*ld[i])%mod;
}
while(pos<=r)ans=(ans+rd[pos]*(mid-l+1))%mod,pos++;
}
signed main()
{
n=read(); for(int i=1;i<=n;i++) s[i]=read();
Binary(1,n); printf("%lld",ans);
return 0;
}

T3 第负二题

解题思路

打了一个 \(\mathcal{O}(n^2)\) 的假做法,被 zero4338 Hack 了。

现在正在努力地学习 \(\mathcal{O}(n)\) 做法。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e6+10,mod=998244353;
int n,m,tim,cnt,out,L,X,Y,Ans;
ull A,B;
int l[N],r[N],lasl[N],lasr[N],ans[N];
bool las[N];
ull xorshift128p() {
ull T = A, S = B;
A = S;
T ^= T << 23;
T ^= T >> 17;
T ^= S ^ (S >> 26);
B = T;
return T + S;
}
void init() {
for (int i = 1; i <= n; i ++) {
l[i] = xorshift128p() % L + X;
r[i] = xorshift128p() % L + Y;
if (l[i] > r[i]) swap(l[i], r[i]);
}
}
signed main()
{
cnt=n=read();L=read();X=read();Y=read();scanf("%llu%llu",&A,&B);init();
las[0]=las[n+1]=true;
for(int i=1;i<=n/2+(n&1)&&cnt;i++)
{
for(int j=i-1;j<=n-i+2;j++)
if(!ans[j]) lasl[j]=l[j],lasr[j]=r[j];
else las[j]=true;
for(int j=i;j<=n-i+1;j++)
{
if(ans[j]) continue;
if(las[j-1]||las[j+1]){ans[j]=i;cnt--;continue;}
l[j]=max(l[j]+1,max(lasl[j-1],lasl[j+1]));
r[j]=min(r[j]-1,min(lasr[j-1],lasr[j+1]));
if(l[j]>r[j]) ans[j]=i,cnt--;
}
}
for(int i=1,base=1;i<=n;i++,base=base*3%mod)
Ans=(Ans+base*ans[i]%mod)%mod;
printf("%lld",Ans);
return 0;
}

NOIP模拟50的更多相关文章

  1. Noip模拟50 2021.9.10

    已经好长时间没有考试不挂分的良好体验了... T1 第零题 开场数据结构,真爽 对于这道题首先要理解对于一条链从上向下和从下向上走复活次数相等 (这可能需要晚上躺在被窝里面脑摸几种情况的样例) 然后就 ...

  2. 2021.9.9考试总结[NOIP模拟50]

    T1 第零题 神秘结论:从一个点满体力到另一个点的复活次数与倒过来相同. 于是预处理出每个点向上走第$2^i$个死亡点的位置,具体实现可以倍增或二分. 每次询问先从两个点同时向上倍增,都转到离$LCA ...

  3. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  4. CH Round #49 - Streaming #4 (NOIP模拟赛Day2)

    A.二叉树的的根 题目:http://www.contesthunter.org/contest/CH%20Round%20%2349%20-%20Streaming%20%234%20(NOIP 模 ...

  5. 11.7 NOIP模拟赛

    目录 2018.11.7 NOIP模拟 A 序列sequence(two pointers) B 锁lock(思路) C 正方形square(埃氏筛) 考试代码 B C 2018.11.7 NOIP模 ...

  6. NOIP模拟赛-2018.11.7

    NOIP模拟赛 如果用命令行编译程序可以发现没加头文件之类的错误. 如果用命令行编译程序可以发现没加头文件之类的错误. 如果用命令行编译程序可以发现没加头文件之类的错误. 编译之前另存一份,听说如果敲 ...

  7. NOIP模拟题汇总(加厚版)

    \(NOIP\)模拟题汇总(加厚版) T1 string 描述 有一个仅由 '0' 和 '1' 组成的字符串 \(A\),可以对其执行下列两个操作: 删除 \(A\)中的第一个字符: 若 \(A\)中 ...

  8. 2016-06-19 NOIP模拟赛

          2016-06-19 NOIP模拟赛 by coolyangzc 共3道题目,时间3小时 题目名 高级打字机 不等数列 经营与开发 源文件 type.cpp/c/pas num.cpp/c ...

  9. 2014-10-31 NOIP模拟赛

        10.30 NOIp  模拟赛   时间 空间 测试点 评测方式 挖掘机(dig.*) 1s 256M 10 传统 黑红树(brtree.*) 2s 256M 10 传统 藏宝图(treas. ...

随机推荐

  1. SaltStack 水平权限绕过漏洞(CVE-2020-11651)

    影响版本 SaltStack < 2019.2.4 SaltStack < 3000.2 poc git clone https://github.com/jasperla/CVE-202 ...

  2. DC-4靶机

    仅供个人娱乐 靶机信息 下载地址:http://www.five86.com/downloads/DC-4.zip 一.主机扫描 arp-scan -l nmap -p 1-65535 -A -sV ...

  3. java获取日出日落时间

    import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; imp ...

  4. 《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上卷(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath.xpath 的定位方法, 非常强大.  使用这种方法几乎可以定位到页面上的任意元素. ...

  5. window.location.href下载文件,文件名中文乱码处理

    下载文件方法: window.location.href='http://www.baidu.com/down/downFile.txt?name=资源文件'; 这种情况下载时:文件名资源文件会中文乱 ...

  6. [洛谷P3376题解]网络流(最大流)的实现算法讲解与代码

    [洛谷P3376题解]网络流(最大流)的实现算法讲解与代码 更坏的阅读体验 定义 对于给定的一个网络,有向图中每个的边权表示可以通过的最大流量.假设出发点S水流无限大,求水流到终点T后的最大流量. 起 ...

  7. Http Request Smuggling - Note

    http请求走私漏洞 访问Burp靶场速度感人..都要哭了(如果没有账户的先创建账户) 基础补充 pipeline http1.1有了Pipeline,就不需要等待Server端的响应了.浏览器默认不 ...

  8. 新手安装eclipse或idea后进行配置、快捷键、插件总结

    .personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...

  9. 漏洞复现|Dubbo反序列化漏洞CVE-2019-17564

    01漏洞描述 - Apache Dubbo支持多种协议,官方推荐使用Dubbo协议.Apache Dubbo HTTP协议中的一个反序列化漏洞(CVE-2019-17564),该漏洞的主要原因在于当A ...

  10. NOIP 模拟 6 大佬

    这道题是一道数学期望,考场上想的是,每相邻 \(k\) 天之间有 \(k-1\) 天是重合的,所以每两端之间肯定是有影响的. 结果啪啪打脸 这道题其实不用考虑每两段之间的影响,因为在上一段的每种排法, ...