CSP-S模拟3
下发文件和题解
A
score and rank (score and rank)
当S是负数时,显然很好想,只需要统计一下有多少个值大于等于S.
当S是正数时,求区间和的最大值f[i]显然等于max(f[i-1],0)+v[i]. 那么可以用一个优先队列来维护区间和,在大于等于S的时候删除最大数. 即把这个数插入优先队列中,如果其内部所有元素的和大于等于S,那么一直弹出最大值并使答案加1,知道和小于S为止.
这样做是伪的. 为什么呢?因为如果出现一个绝对值极大的负数,使得元素和小于0了,这样的数是一定不会被选择的. 所以这时候就要把堆里的最小值不断弹出,直至弹空或弹到抵消掉这个数为止. 可能会出现最后弹出的和不能等于这个数,就把差值加入就可以了.
既要弹最小值,又要弹最大值,可以选择用一个可重set或者两个优先队列. 这里我用的是可重set.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 1000001
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,s;
ll a,ans;
multiset<ll> q;
ll sum;
int main()
{
n=read();s=read();
if(s<0) { while(n--) ans+=read()>=s;write(ans);return 0; }
for(rll i=1;i<=n;i++)
{
a=read();
sum+=a;
if(a>0)
{
rg bool fl=0;
q.insert(a);
while((!q.empty())&&sum>=s)
{
rll t=*(--q.end());
sum-=t;q.erase(--q.end());ans++;
}
}
else
{
if(sum<0) { sum=0;q.clear(); }
else
{
rll su=sum-a,kk=0;
while((!q.empty())&&su>sum)
{
rll t=*q.begin();if(su-t<sum) kk=sum-su+t;
su-=t;q.erase(q.begin());
}
if(kk) q.insert(kk);
}
}
}
write(ans);
return 0;
}
B
HZOI大作战 (收藏)
既然是一棵树,而且是从下往上走,那么每一次走的路线是一定的,就是走的是哪几个点是不会变的.
要求的是最大值,对于 小于等于c的必定不会对答案产生贡献. 那么 只需要找到第一个 大于c的i,重新建树,把出发的节点直接连到i上. 后面也是一样,只不过c就成为了上次的 . 如果c比开始节点的a要小的话一定会在第一个点就选,这样可以直接在原树上跑一遍结果加1就完了. 对于末尾如果跳到的深度比终点的深度相等或者还要大,也把结果加一个1.
点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 500001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,q,mx,u,v,c;
ll a[maxn],dep[maxn],f[maxn],fa[maxn][20];
vector<ll> g[maxn];
static inline void dfs1(rll x)
{// 处理出父亲和深度
for(rll i=0;i<g[x].size();i++)
{
rll to=g[x][i];if(to==f[x]) continue;
dep[to]=dep[x]+1;f[to]=x;dfs1(to);
}
}
static inline void dfs2(rll x)
{
rll t=f[x];// 设当前位于u,要找到第一个a_i大于a_u的i
for(rll i=mx;i+1;i--) if(a[x]>=a[fa[t][i]]) t=fa[fa[t][i]][0];
if(a[x]>=a[t]) fa[x][0]=fa[t][0];else fa[x][0]=t;
// 重新建图,把u直接连到i上
for(rll i=1;i<=mx;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(rll i=0;i<g[x].size();i++)
{
rll to=g[x][i];if(to==f[x]) continue;dfs2(to);
}
}
static inline ll getans(rll x,rll y)
{
rll ans=0;
for(rll i=mx;i+1;i--) if(dep[fa[x][i]]>=dep[y]) ans+=(1<<i),x=fa[x][i];
return ans;
}
int main()
{
n=read();q=read();mx=ceil(log2(n));a[0]=INT_MAX;
for(rll i=1;i<=n;i++) a[i]=read();
for(rll i=1;i<n;i++) u=read(),v=read(),g[u].push_back(v),g[v].push_back(u);
dep[1]=1;dfs1(1);dfs2(1);
while(q--)
{
u=read();v=read();c=read();
if(c<a[u]) { write(getans(u,v)+1); putn; continue; }// 特判首:c比首个数还小,必是直接在树上跑的大小加1
for(rll i=mx;i+1;i--) if(dep[fa[u][i]]>=dep[v]&&a[fa[u][i]]<=c) u=fa[u][i];// 维护深度,找到第一个大于c的位置
u=fa[u][0];
write(getans(u,v)+(dep[u]>=dep[v]/*特判尾*/));putn;
}
return 0;
}
C
Delov的旅行 (小 S 的旅行)
暂时咕.
D
gtm和joke的星球 (小可爱的星球)
最小斯坦纳树的模板题.
这里有篇参考博客:参考博客
(后面我会整理出一篇)
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 501
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rll f=0,x=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
ll n,m,k,ans=LLONG_MAX;
ll dp[maxn][1<<11];
ll sel[maxn];
ll dis[maxn];
bool fl[maxn];
vector<pll> g[maxn];
queue<ll> q;
static inline void spfa(rll S)
{
for(rll i=1;i<=n;i++)
{
dis[i]=dp[i][S];
if(dis[i]!=0x3f3f3f3f3f3f3f3f) q.push(i),fl[i]=1;
}
while(!q.empty())
{
rll t=q.front();q.pop();fl[t]=0;
for(rll i=0;i<g[t].size();i++)
{
rll to=g[t][i].first;
if(dis[to]>dis[t]+g[t][i].second)
{
dis[to]=dis[t]+g[t][i].second;
if(!fl[to]) q.push(to),fl[to]=1;
}
}
}
for(rll i=1;i<=n;i++) dp[i][S]=dis[i];
}
int main()
{
memset(dp,0x3f,sizeof(dp));
n=read();m=read();k=read();
for(rll i=1,u,v,w;i<=m;i++) u=read(),v=read(),w=read(),g[u].push_back((pll) { v,w }),g[v].push_back((pll) { u,w });
for(rll i=1;i<=k;i++) sel[i]=read(),dp[sel[i]][1<<i-1]=0;
for(rll i=1;i<=n;i++) dp[i][0]=0;
for(rll S=0;S<1<<k;S++)
{
for(rll i=1;i<=n;i++)
for(rll T=S&(S-1);T;T=S&(T-1))//结论,可以枚举到S的全部子集(比如集合101就可以枚举到100、001,直到000为止)
dp[i][S]=min(dp[i][S],dp[i][T]+dp[i][S^T/*∁ₛᵀ*/]);
spfa(S);
}
for(rll i=1;i<=n;i++) ans=min(ans,dp[i][(1<<k)-1]);
write(ans);
return 0;
}
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16666998.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!