CSP 2022 提高组 题解
目录
A.假期计划
B.策略游戏
C.星战
D.数据传输
A.假期计划
题目描述
给定一张 个点, 条边的无向图,点权 。
你需要构造一条路径 ,满足 互不相同,并且相邻两点在原图上可以经过不超过 个中转点(不包含两个端点)到达。
求 的最大值。
数据范围
- ,保证至少存在一条合法路径。
时间限制 ,空间限制 。
分析
注意到图的规模很小,用 可以在 的时间内求出任意两点是否满足在原图上的距离 。
对每个点 ,记录 的前三大的二元组 。
枚举中间的边 ,将这 对 检查互异性后更新答案。
时间复杂度 。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=2505;
int k,m,n,u,v,res;
int d[maxn],w[maxn];
pii f[maxn][3];
bool b[maxn][maxn];
vector<int> g[maxn];
signed main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=2;i<=n;i++) scanf("%lld",&w[i]);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
queue<int> q;
for(int j=1;j<=n;j++) d[j]=-1;
d[i]=0,q.push(i);
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto v:g[u]) if(d[v]==-1) d[v]=d[u]+1,q.push(v);
}
for(int j=1;j<=n;j++) b[i][j]=d[j]>=0&&d[j]<=k+1;
}
for(int i=1;i<=n;i++)
for(int j=2;j<=n;j++)
{
if(j==i||!b[1][j]||!b[j][i]) continue;
pii cur=mp(w[i]+w[j],j);
if(cur>f[i][0]) f[i][2]=f[i][1],f[i][1]=f[i][0],f[i][0]=cur;
else if(cur>f[i][1]) f[i][2]=f[i][1],f[i][1]=cur;
else if(cur>f[i][2]) f[i][2]=cur;
}
for(int u=1;u<=n;u++)
for(int v=u+1;v<=n;v++)
{
if(!b[u][v]) continue;
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
{
int x=f[u][i].se,y=f[v][j].se;
if(x&&y&&x!=v&&y!=u&&x!=y) res=max(res,f[u][i].fi+f[v][j].fi);
}
}
printf("%lld\n",res);
return 0;
}
B.策略游戏
题目描述
给定一个长为 的数组 ,和一个长为 的数组 。
次询问,给定 ,求 。
数据范围
- 。
- 。
时间限制 ,空间限制 。
分析
对于 ,显然 只会选择最小值。
对于 ,显然 只会选择最大值。
将 和 的下标 分开维护,根据 的正负性我们可以知道 应该尽量小还是尽量大。
用 表维护区间最大最小值,时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,inf=1e9+5;
int m,n,q,l1,r1,l2,r2;
int a[maxn],b[maxn],lg[maxn];
bool c[maxn];
struct node
{
int f[17][maxn],g[17][maxn];
int query(int l,int r,int op)
{
int k=lg[r-l+1];
return !op?min(f[k][l],f[k][r-(1<<k)+1]):max(g[k][l],g[k][r-(1<<k)+1]);
}
void init(int n,int *a,bool *b)
{
for(int i=1;i<=n;i++) f[0][i]=b[i]?a[i]:inf,g[0][i]=b[i]?a[i]:-inf;
for(int j=1;j<=16;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
g[j][i]=max(g[j-1][i],g[j-1][i+(1<<(j-1))]);
}
}
}t0,t1,t2;
long long mul(int x,int y)
{
if(abs(y)==inf) return -1e18;
return 1ll*x*y;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
for(int i=2;i<=max(n,m);i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=m;i++) c[i]=1;
t0.init(m,b,c);
for(int i=1;i<=n;i++) c[i]=a[i]>=0;
t1.init(n,a,c);
for(int i=1;i<=n;i++) c[i]=a[i]<=0;
t2.init(n,a,c);
while(q--)
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
int x=t0.query(l2,r2,0),y=t0.query(l2,r2,1);
printf("%lld\n",max(mul(x,t1.query(l1,r1,x>=0)),mul(y,t2.query(l1,r1,y>=0))));
}
return 0;
}
C.星战
题目描述
给定一张 个点, 条边的有向图,初始所有边都被激活。
接下来 次操作:
1 u v
:失活一条 的边,保证这条边操作前处于激活状态。2 u
:失活以 为终点的所有边。3 u v
:激活一条 的边,保证这条边操作前处于失活状态。4 u
:激活以 为终点的所有边。
在每次操作后,询问是否满足每个点恰有一条出边被激活。
数据范围
- 。
时间限制 ,空间限制 。
分析
观察本题的操作,发现更容易维护的是入度而不是出度。
将所有以 为起点的被激活的边全部赋值 ,其中 为关于 的随机数。
记第 个点的入度为 ,注意到每条边恰好贡献一个出度和一个入度,因此我们可以维护以 为终点的边的权值和,也就相当于维护了 的值。
由于 的非负整数解 很大概率只有 这一组,因此我们可以认为出现这种情况等价于每个点出度均为一。
时间复杂度 。
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int maxn=5e5+5;
int m,n,q,u,v,op;
ull all,cur;
ull f[maxn],g[maxn],w[maxn];
mt19937_64 rnd(time(0));
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) w[i]=rnd(),all+=w[i];
while(m--) scanf("%d%d",&u,&v),g[v]+=w[u];
for(int i=1;i<=n;i++) f[i]=g[i],cur+=f[i];
scanf("%d",&q);
while(q--)
{
scanf("%d",&op);
if(op==1) scanf("%d%d",&u,&v),cur-=w[u],f[v]-=w[u];
if(op==2) scanf("%d",&u),cur-=f[u],f[u]=0;
if(op==3) scanf("%d%d",&u,&v),cur+=w[u],f[v]+=w[u];
if(op==4) scanf("%d",&u),cur+=g[u]-f[u],f[u]=g[u];
printf(cur==all?"YES\n":"NO\n");
}
return 0;
}
D.数据传输
题目描述
给定一棵 个节点的树和常数 ,点有点权 。
次询问,每次给定 。
你需要构造一个序列 (记长度为 ),满足 ,求 的最小可能值。
数据范围
- 。
- 。
时间限制 ,空间限制 。
分析
对于 ,输出树上带权路径和即可。
对于 ,容易发现我们只会经过 树上路径中的点。
表示考虑到树上第 个点,是否在 中出现, 的最小可能值。
记路径中上一个点为 ,转移方程如下:
对于 ,我们不一定只经过树上路径中的点,还可以通过路径上某个点的邻点来绕路!
显然如果要绕路一定只会选择第个点的邻点中的最小权值,不妨记为 。
表示考虑到树上第 个点,上一个在 中出现的点到 的距离为 , 的最小可能值。
如果 刚好也在 路径上也没关系,因为这种情况绕路一定不优。
记路径中上一个点为 ,转移方程如下:
容易发现上述转移都可以用 矩阵刻画,维护倍增向上和倍增向下的矩阵连乘积即可。
时间复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5,inf=1e18;
int m,n,q,u,v;
int d[maxn],w[maxn],fa[maxn][18];
vector<int> g[maxn];
struct mat
{
int v[3][3];
}x,y,a[maxn],up[maxn][18],dn[maxn][18];
inline mat operator*(const mat &a,const mat &b)
{
static mat c;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
{
c.v[i][j]=inf;
for(int k=0;k<m;k++) c.v[i][j]=min(c.v[i][j],a.v[i][k]+b.v[k][j]);
}
return c;
}
void dfs(int u,int f)
{
for(auto v:g[u])
{
if(v==f) continue;
d[v]=d[u]+1,fa[v][0]=u,dn[v][0]=up[v][0]=a[v];
for(int i=1;i<=17;i++)
{
fa[v][i]=fa[fa[v][i-1]][i-1];
dn[v][i]=dn[fa[v][i-1]][i-1]*dn[v][i-1];
up[v][i]=up[v][i-1]*up[fa[v][i-1]][i-1];
}
dfs(v,u);
}
}
int lca(int u,int v)
{
if(d[u]<d[v]) swap(u,v);
for(int i=17;i>=0;i--)
if(d[fa[u][i]]>=d[v])
u=fa[u][i];
if(u==v) return u;
for(int i=17;i>=0;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
signed main()
{
scanf("%lld%lld%lld",&n,&q,&m);
for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
for(int i=1;i<=n-1;i++)
{
scanf("%lld%lld",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
if(m==1) for(int i=1;i<=n;i++) a[i].v[0][0]=w[i];
if(m==2) for(int i=1;i<=n;i++) a[i].v[0][0]=a[i].v[1][0]=w[i],a[i].v[1][1]=inf;
if(m==3) for(int i=1;i<=n;i++)
{
int mn=inf;
for(auto v:g[i]) mn=min(mn,w[v]);
a[i]={w[i],0,inf,w[i],mn,0,w[i],inf,inf};
}
d[1]=1,dfs(1,0);
mat tmp={0,inf,inf,inf,0,inf,inf,inf,0};
while(q--)
{
scanf("%lld%lld",&u,&v),x=y=tmp;
int p=lca(u,v);
for(int i=17;i>=0;i--)
{
if(d[fa[u][i]]>=d[p]) x=x*up[u][i],u=fa[u][i];
if(d[fa[v][i]]>=d[p]) y=dn[v][i]*y,v=fa[v][i];
}
printf("%lld\n",(x*a[p]*y).v[m-1][0]);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17316021.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现