T1 嗑瓜子

解题思路

\(f_{i,j}\) 表示操作 \(i\) 次,拿走了 \(j\) 个瓜子的概率,转移就比较直接了:

\[f_{i+1,j+1}\leftarrow f_{i,j}\times\dfrac{n-j}{n+2\times j-i}
\]
\[f_{i+1,j}\leftarrow f_{i,j}\times\dfrac{3\times j-i}{n+2\times j-i}
\]

这里如果边界卡不准的话可能会出现使分母出现负数,注意一下,不然就会 RE 获得 50pts 的高分。。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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=2e3+10,mod=998244353;
int n,ans,f[N*3][N],inv[N*3];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int Inv(int x){if(x>=0)return inv[x];return 0;}
#undef int
int main()
{
#define int long long
freopen("eat.in","r",stdin); freopen("eat.out","w",stdout);
n=read(); f[0][0]=1; inv[1]=1;
for(int i=2;i<=3*n;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(int i=0;i<3*n-2;i++)
for(int j=0;j<=min(i,n-1);j++)
add(f[i+1][j+1],f[i][j]*(n-j)%mod*Inv(n+2*j-i)%mod),
add(f[i+1][j],f[i][j]*(3*j-i)%mod*Inv(n+2*j-i)%mod);
for(int i=n;i<=3*n-2;i++) add(ans,f[i][n]*i%mod);
printf("%lld",ans);
return 0;
}

T2 第 k 大查询

解题思路

在区间 \([l,r]\) 中 \(s_i\) 是 k 大值的情况当且仅当 \([l,r]\) 中有 \(k-1\) 个大于 \(s_i\) 的数字。

那么我们就可以维护一下每一个数字前面比 \(s_i\) 大的 k 个值以及后面比 \(s_i\) 大的 k 个值,然后指针扫一遍就好了。

那么问题就变成的如何维护一个数前面以及后面的比他大的值,我们可以选择 双向链表 来实现。

对于值域上面按照原序列顺序建一个链表,然后从小往大计算值的贡献,每次计算完之后直接删掉这个值就可以了。

code

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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=5e5+10;
int n,m,t1,t2,a[N],b[N],s[N],id[N],fro[N],nxt[N];
ll ans;
#undef int
int main()
{
#define ll long long
freopen("kth.in","r",stdin); freopen("kth.out","w",stdout);
n=read(); m=read(); id[s[0]=n+1]=0; id[s[n+1]=n+2]=n+1;
for(int i=1;i<=n;i++) s[i]=read(),id[s[i]]=i;
for(int i=1;i<=n;i++) fro[s[i]]=s[i-1],nxt[s[i]]=s[i+1];
for(int i=1;i<=n;i++)
{
t1=t2=-1;
for(int j=i;j&&t1<m;j=fro[j]) a[++t1]=id[j];
for(int j=i;j&&t2<m;j=nxt[j]) b[++t2]=id[j];
for(int j=1;j<=t1;j++) if(m-j<t2) ans+=1ll*i*(a[j-1]-a[j])*(b[m-j+1]-b[m-j]);
int tmp1=fro[i],tmp2=nxt[i]; fro[tmp2]=tmp1,nxt[tmp1]=tmp2;
}
printf("%lld",ans);
return 0;
}

T3 树上路径

解题思路

一道及其难调的树形 DP ,需要维护好多的值QAQ。。。

\(f_i\) 表示 \(i\) 节点向子树中可以延伸到最长路径。

\(dis_i\) 表示 \(i\) 节点为根节点的子树中最长的路径。

\(g_i\) 表示 除了以 \(i\) 节点为根的子树,\(fa_i\) 为路径的一个端点可以延伸到最长路径。

\(dp_i\) 表示删去 \(i\rightarrow fa_i\) 这一条边 \(fa_i\) 所在部分的最长路径。

答案显然就是对于每一个边枚举它断掉的情况,然后记录两个部分的直径(假设深度较大的是 \(x\),两个直径就是 \(dis_x\) 和 \(dp_x\))最后再给答案取一个后缀 \(\max\) 。

对于 \(f_i\) 还有 \(dis_i\) 数组的计算其实就是 DP 求树的直径的打法。

对于 \(g_i\) 数组的直接记录一个子树内的最大值以及次大值就好了。

对于 \(dp_i\) 数组的转移需要记一下关于 \(f_i\) 的最大值次大值以及次次大值,因为计算答案的时候需要算上除了自己这一棵子树,它的父亲节点的其他子树之间的最长路径所拼起来的贡献。

大概三遍 DFS 就解决了。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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=5e5+10;
int n,Ans,ans[N],f[N],g[N],dis[N],dep[N],mx[N],sec[N],tri[N],dp[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1];
bool vis[N];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue; dfs(ver[i],x);
dis[x]=max(dis[x],max(dis[to],f[x]+f[to]+1));
f[x]=max(f[x],f[to]+1);
if(f[to]+1>mx[x]) tri[x]=sec[x],sec[x]=mx[x],mx[x]=f[to]+1;
else if(f[to]+1>sec[x]) tri[x]=sec[x],sec[x]=f[to]+1;
else tri[x]=max(tri[x],f[to]+1);
}
}
void dfs2(int x,int fa)
{
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue;
if(f[to]+1==mx[x]) g[to]=max(g[x]+1,sec[x]+1);
else g[to]=max(g[x]+1,mx[x]+1); dfs2(ver[i],x);
}
}
void dfs3(int x,int fa)
{
int num1=0,num2=0;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue;
if(num1<dis[to]+1) num2=num1,num1=dis[to]+1;
else num2=max(num2,dis[to]+1);
}
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue; dp[to]=g[x];
if(f[to]+1==mx[x]) dp[to]=max(dp[to],sec[x]);
else dp[to]=max(dp[to],mx[x]);
}
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue;
dp[to]=max(dp[to],dis[to]+1==num1?num2:num1);
if(f[to]+1==mx[x]) dp[to]=max(dp[to],sec[x]+g[x]+1);
else dp[to]=max(dp[to],mx[x]+g[x]+1);
if(f[to]+1==mx[x]) dp[to]=max(dp[to],sec[x]+tri[x]+1);
else if(f[to]+1==sec[x]) dp[to]=max(dp[to],mx[x]+tri[x]+1);
else dp[to]=max(dp[to],mx[x]+sec[x]+1); dfs3(to,x);
}
}
#undef int
int main()
{
#define int long long
freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
n=read();
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),
add_edge(x,y),add_edge(y,x);
dfs(1,0); dfs2(1,0); dfs3(1,0);
for(int i=1;i<n;i++)
{
int x=ver[i<<1],y=ver[i<<1|1]; if(dep[x]>dep[y]) swap(x,y);
int num1=dis[y]+1,num2=dp[y];
ans[num1]=max(ans[num1],num2); ans[num2]=max(ans[num2],num1);
}
for(int i=n;i>=1;i--) ans[i]=max(ans[i],ans[i+1]),Ans+=ans[i];
printf("%lld",Ans);
return 0;
}

T4 糖

解题思路

思路特别妙,我们先在每一个点把背包填满,那么如果到了最后还没有用掉的话可以直接按照原价卖出计算贡献。

如果背包中糖果的买入价格小于当前点的卖价,我们就可以当做他们是以当前点卖价买进来的,于是可以直接更改他们的价格但是不对答案造成贡献。

那么如果背包中糖果的买入价格大于当前点的买入价,我们就可以直接替换掉这些糖果。

最后再把背包填满,然后计算路径上的删掉的糖果,优先吃掉买的代价较小的糖果。

上述操作都可以通过 单调队列 来实现。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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,head=1,tail,ans,hav,s[N],a[N],b[N];
struct Node{int val,cnt;}q[N];
#undef int
int main()
{
#define int long long
freopen("candy.in","r",stdin); freopen("candy.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++) s[i]=read();
for(int i=0;i<n;i++) a[i]=read(),b[i]=read();
for(int i=0;i<n;i++)
{
int sum=0,dis=s[i+1]-s[i];
while(head<=tail&&q[head].val<=b[i]) sum+=q[head++].cnt;
if(sum) q[--head]=(Node){b[i],sum};
while(head<=tail&&q[tail].val>=a[i])
ans-=q[tail].val*q[tail].cnt,hav-=q[tail--].cnt;
if(hav<m) q[++tail]=(Node){a[i],m-hav},ans+=a[i]*(m-hav),hav=m;
while(head<=tail&&dis>=q[head].cnt) dis-=q[head++].cnt;
if(dis) q[head].cnt-=dis; hav-=s[i+1]-s[i];
}
while(head<=tail) ans-=q[head].cnt*q[head].val,head++;
printf("%lld",ans);
return 0;
}

NOIP模拟95(多校28)的更多相关文章

  1. NOIP模拟83(多校16)

    前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...

  2. NOIP模拟92(多校25)

    前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...

  3. noip模拟题 2017.10.28 -kmp -Tarjan -鬼畜的优化

    题目大意 给定A串,选择A串的前lB个字符作为B串,再在B串后增加一个字符,问最长的相等的A串前缀和B串的后缀. Solution 1(KMP) 用1个奇怪的字符连接A串和B串,再用KMP求最长公共前 ...

  4. NOIP模拟84(多校17)

    T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...

  5. NOIP模拟85(多校18)

    前言 好像每个题目背景所描述的人都是某部番里的角色,热切好像都挺惨的(情感上的惨). 然后我只知道 T1 的莓,确实挺惨... T1 莓良心 解题思路 首先答案只与 \(w\) 的和有关系,于是问题就 ...

  6. NOIP模拟86(多校19)

    T1 特殊字符串 解题思路 \(f_{i,j}\) 表示前 \(i\) 个字符中结尾为 \(j\) 的最大贡献. 转移枚举当前位置于之前位置结尾的组合加上贡献即可. 对于边界问题,容易发现选择 1 一 ...

  7. NOIP模拟88(多校21)

    前言 对于这套题的总体感觉就是难,然后就是自己很菜... 对于 T1 考试时只会一个最垃圾的背包,考完之后对于思路这一块也不是很顺利,大概这就是薄弱的地方吧. 然后 T2 是比较简单的一道题了,但是考 ...

  8. NOIP模拟96(多校29)

    T1 子集和 解题思路 大概是一个退背包的大白板,然而我考场上想复杂了,竟然还用到了组合数. 但是大概意思是一样的,有数的最小值一定是一个在 \(a\) 数组中存在的数字. 那么我们想办法除去它对应的 ...

  9. NOIP模拟99(多校31)

    T1 法阵 解题思路 原题3100,张口放 T1(出题人原话) 思维题,合法的情况其实就是上下两个梯形拼起来的样子. 他们的边界都是在 \(i\) 轴上面,但是不能相交. 于是我们可以尝试两者相交的纵 ...

  10. NOIP模拟 6.28

    NOIP模拟赛6.28 Problem 1 高级打字机(type.cpp/c/pas) [题目描述] 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这 ...

随机推荐

  1. identity4 系列————用户数据持久化篇[六]

    前言 前面的例子已经将各种情形下的例子已经介绍了一遍,那么后面就是用户数据持久化该如何处理了. 正文 例子位置: https://github.com/IdentityServer/IdentityS ...

  2. coco2017 Dataset EDA

    Github仓库:gy-7/coco_EDA (github.com) 对coco数据集的分析,近期忙着写论文,空余时间很少能写博文了. EDA的代码放在结尾了,Github仓库里也有.仓库里还有其他 ...

  3. 深度解读《深度探索C++对象模型》之返回值优化

    接下来我将持续更新"深度解读<深度探索C++对象模型>"系列,敬请期待,欢迎关注!也可以关注公众号:iShare爱分享,自动获得推文和全部的文章列表. 没有启用返回值优 ...

  4. Redis基础(一)——字符串、hash类型的基本使用

    day09--Redis Redis介绍和安装 # Redis:软件,存储数据的,速度非常快,Redis是一个key-value存储系统(没有表的概念),cs架构的软件 服务端 客户端(python作 ...

  5. stm32串口晶振不对输出乱码+汇承HC-14lora模块

    最近要用到一个lora无线透传模块,然后就先用两个32开发板(用的STM32F103C8T6)试试简单的收发数据.结果,第一步串口发送一句话就寄了,我串口打印了"hi",结果出现了 ...

  6. 力扣693(java)-交替位二进制数(简单)

    题目: 给定一个正整数,检查它的二进制表示是否总是 0.1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同. 示例 1: 输入:n = 5输出:true解释:5 的二进制表示是:101示例 ...

  7. 同为博客,不同风格 ——Hexo另类搭建

    ​简介:通过阿里云云开发平台快速由Hexo创建赛博朋克风格的博客. 一  .通过云开发平台快速创建初始化应用 1.创建相关应用模版请参考链接:Hexo博客框架-轻量.一令部署 2.完成创建后就可以在g ...

  8. [LLM] 开源 AI 大语言模型的本地化定制实践

      LLM(Large Language Model,大型语言模型)是一种基于深度学习的自然语言处理模型,旨在理解和生成人类语言. 它们在大量的文本数据上进行训练,可以执行广泛的任务,包括文本总结.翻 ...

  9. [ML] 通过llama.cpp与羊驼聊天的网页界面- 详解 Serge 的启动使用

      Serge 虽然能够让我们在笔记本上跑起来 7B模型, 但实际运行非常消耗CPU,对话生成响应非常非常慢. 1. 官方指导是使用如下命令直接运行: $ docker run -d -v weigh ...

  10. dotnet SemanticKernel 入门 注入日志

    使用 SemanticKernel 框架在对接 AI 时,由于使用到了大量的魔法,需要有日志的帮助才好更方便定位问题,本文将告诉大家如何在 SemanticKernel 注入日志 本文属于 Seman ...