加载中…

返回上一页

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;
}
posted @ 2022-09-11 14:00  1Liu  阅读(73)  评论(0)    收藏  举报