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. 力扣613(MySQL)-直线上的最近距离(简单)

    题目: 表 point 保存了一些点在 x 轴上的坐标,这些坐标都是整数. 写一个查询语句,找到这些点中最近两个点之间的距离. 最近距离显然是 '1' ,是点 '-1' 和 '0' 之间的距离.所以输 ...

  2. 4.CSS层次选择器

    1.后代选择器:在某个元素的后面 1 /*后代选择器*/ 2 body p{ 3 background: crimson; 4 } 2.子选择器:仅一代 1 /*子选择器*/ 2 body>p{ ...

  3. K8s 网关选型初判:Nginx 还是 Envoy?

    简介: 本文将从性能和成本.可靠性.安全性 3 方面,对两大开源实现进行比对,希望对正在做 K8s 网关选型的企业有所借鉴. 作者:张添翼(澄潭) 为了避免混淆,我们先对一些关键定义做一些厘清: 传统 ...

  4. DDD as Code:如何用代码诠释领域驱动设计?

    简介: 相较于常规的MVC架构,DDD更抽象.更难以理解,各个开发者对DDD的解释也不尽相同.那么哪种设计方式才更好?在学习时如何知道哪种DDD更正统,没有被别人带歪?本文尝试使用"DDD ...

  5. 源码解读:KubeVela 是如何将 appfile 转换为 K8s 特定资源对象的

    简介: KubeVela 是一个简单易用又高度可扩展的云原生应用管理引擎,是基于 Kubernetes 及阿里云与微软云共同发布的云原生应用开发模型 OAM 构建.本文主要目的是探索 KubeVela ...

  6. 如何高效完成ECS多环境部署?

    ​简介:通过本文,你可以了解到,如何通过云效流水线有效拉通开发与运维,打破二者之间的壁垒墙,让开发与运维高效联动.在软件开发和部署过程中,我们的软件往往需要在不同的运行环境中运行,例如:开发人员本地开 ...

  7. 阿里云贾扬清:大数据+AI工程化,让数据从「成本」变为「资产」

    简介: 近年来,数字经济发展迅速,企业转型背后频频涌现「数字力量」的身影.云计算.大数据.人工智能的快速融合形成了数字经济的新基建,也为数字经济发展带来了新的机遇. 5 月 20 日,阿里巴巴副总裁. ...

  8. [FAQ] web3js, Error: Please pass numbers as strings or BN objects to avoid precision errors.

    我们在调用合约方法时,都可以传一些参数的,比如转账金额 value. value 的单位是 wei,这是一个很小的单位,所以一般数值很大. 注意,把 ether 转 wei 需要先把 ether 的值 ...

  9. CPU是什么?

    在程序是怎样跑起来的这本书中我们首先被询问的一个问题是"程序是什么?它是有什么组成的?而CPU又与程序有什么关系呢?",若我们能知道前两个,其实更容易将你带入讨论"CPU ...

  10. LLaMA 3 源码解读-大语言模型5

    本来不是很想写这一篇,因为网上的文章真的烂大街了,我写的真的很有可能没别人写得好.但是想了想,创建这个博客就是想通过对外输出知识的方式来提高自身水平,而不是说我每篇都能写得有多好多好然后吸引别人来看. ...