CSP-S 2022 游记
真就游寄
虽然没能正常参加 CSP,但是还是要做一下。
说实话这成绩七级勾本应该是稳了
T1 假期计划 (\(holiday\))2s 512M
\(1\to A\to B\to C\to D\to 1\)
观察这个结构,发现两边是对称的。
预处理出对于所有 \(B\), \(1\to A \to B\) 的最大的 \(A\) 的值。
枚举一下 \(B,C\),直接算贡献。
考虑到点冲突的问题,要记录次大值和次次大值。
int n, m, k;
struct node {
int t, n;
}e[100005];
int u, v;
int head[100005], head_size;
void ADD(int f, int t) {
e[++head_size]=(node){t, head[f]};
head[f]=head_size;
}
int dis[2505][2505];
ll val[2505], ans;
ll maxx[2505][3];
void Change(int i, int j) {
if(val[maxx[i][0]]<val[j]) {
maxx[i][2]=maxx[i][1];
maxx[i][1]=maxx[i][0];
maxx[i][0]=j;
}
else if(val[maxx[i][1]]<val[j]) {
maxx[i][2]=maxx[i][1];
maxx[i][1]=j;
}
else if(val[maxx[i][2]]<val[j]) {
maxx[i][2]=j;
}
}
void BFS(int S) {
dis[S][S]=0;
queue<int>q;
q.push(S);
while(!q.empty()) {
int x=q.front(); q.pop();
for(int i=head[x]; i; i=e[i].n) {
if(dis[S][e[i].t]>100000000) {
dis[S][e[i].t]=dis[S][x]+1;
q.push(e[i].t);
}
}
}
}
ll slove(int x, int y) {
ll re=-1;
ll sum=val[x]+val[y];
for(int i=0; i<=2; ++i) {
if(maxx[x][i]==0) continue ;
if(maxx[x][i]==y) continue ;
for(int j=0; j<=2; ++j) {
if(maxx[y][j]==0) continue ;
if(maxx[y][j]==x) continue ;
if(maxx[y][j]==maxx[x][i]) continue ;
re=max(re, sum+val[maxx[x][i]]+val[maxx[y][j]]);
}
}
return re;
}
int main(){
memset(dis, 0x3f, sizeof(dis));
n=read(); m=read(); k=read();
for(int i=2; i<=n; ++i) val[i]=read();
for(int i=1; i<=m; ++i) {
u=read(); v=read();
ADD(u, v); ADD(v, u);
}
for(int i=1; i<=n; ++i) BFS(i);
for(int i=2; i<=n; ++i) {
for(int j=2; j<=n; ++j) {
if(i==j) continue ;
if(dis[1][j]<=k+1&&dis[j][i]<=k+1) Change(i, j);
}
}
for(int i=2; i<=n; ++i) {
if(!maxx[i][0]) continue ;
for(int j=2; j<=n; ++j) {
if(i==j) continue ;
if(dis[i][j]>k+1) continue ;
if(!maxx[j][0]) continue ;
ans=max(ans, slove(i, j));
}
}
cout<<ans;
}
T2 策略游戏 ( \(game\) ) 1s 512M
\(A\) 选一个数,\(B\) 选择使答案最小的那个。
对于所有 \(A\) 选数之后 \(B\) 选出的最小值取个最大值就行。
其实再多再多也就四种情况。
选正数里最大的或最小的,选负数里最大的或最小的。
那么两个序列,一共 \(4\times 4 = 16\) 种情况,枚举一下就行了。
特别的, \(0\) 作为交界点,即算正数,也算负数。
然后 \(8\) 个 ST 表伺候上就行了。
int n, m, q, a;
int sta[100005][20][4];
int stb[100005][20][4];
int lo[100005];
/*
0正数最大
1正数最小
2负数最大
3负数最小
*/
int main(){
n=read(); m=read(); q=read();
for(int i=1; i<=n; ++i) {
a=read();
sta[i][0][0]=sta[i][0][2]=-2e9;
sta[i][0][1]=sta[i][0][3]=2e9;
if(a>=0) sta[i][0][0]=sta[i][0][1]=a;
if(a<=0) sta[i][0][2]=sta[i][0][3]=a;
}
for(int i=1; i<=m; ++i) {
a=read();
stb[i][0][0]=stb[i][0][2]=-2e9;
stb[i][0][1]=stb[i][0][3]=2e9;
if(a>=0) stb[i][0][0]=stb[i][0][1]=a;
if(a<=0) stb[i][0][2]=stb[i][0][3]=a;
}
for(int i=2; i<=max(n, m); ++i) lo[i]=lo[i>>1]+1;
for(int cs=1; cs<=lo[n]; ++cs) {
for(int i=1; i+(1<<cs)-1<=n; ++i) {
sta[i][cs][0]=max(sta[i][cs-1][0], sta[i+(1<<cs-1)][cs-1][0]);
sta[i][cs][1]=min(sta[i][cs-1][1], sta[i+(1<<cs-1)][cs-1][1]);
sta[i][cs][2]=max(sta[i][cs-1][2], sta[i+(1<<cs-1)][cs-1][2]);
sta[i][cs][3]=min(sta[i][cs-1][3], sta[i+(1<<cs-1)][cs-1][3]);
}
}
for(int cs=1; cs<=lo[m]; ++cs) {
for(int i=1; i+(1<<cs)-1<=m; ++i) {
stb[i][cs][0]=max(stb[i][cs-1][0], stb[i+(1<<cs-1)][cs-1][0]);
stb[i][cs][1]=min(stb[i][cs-1][1], stb[i+(1<<cs-1)][cs-1][1]);
stb[i][cs][2]=max(stb[i][cs-1][2], stb[i+(1<<cs-1)][cs-1][2]);
stb[i][cs][3]=min(stb[i][cs-1][3], stb[i+(1<<cs-1)][cs-1][3]);
}
}
while(q--) {
int A[4], B[4];
int l, r, cs;
l=read(); r=read(); cs=lo[r-l+1];
A[0]=max(sta[l][cs][0], sta[r-(1<<cs)+1][cs][0]); A[1]=min(sta[l][cs][1], sta[r-(1<<cs)+1][cs][1]);
A[2]=max(sta[l][cs][2], sta[r-(1<<cs)+1][cs][2]); A[3]=min(sta[l][cs][3], sta[r-(1<<cs)+1][cs][3]);
l=read(); r=read(); cs=lo[r-l+1];
B[0]=max(stb[l][cs][0], stb[r-(1<<cs)+1][cs][0]); B[1]=min(stb[l][cs][1], stb[r-(1<<cs)+1][cs][1]);
B[2]=max(stb[l][cs][2], stb[r-(1<<cs)+1][cs][2]); B[3]=min(stb[l][cs][3], stb[r-(1<<cs)+1][cs][3]);
ll ans=-1e18, sum;
for(int i=0; i<4; ++i) {
if(abs(A[i])==2e9) continue ;
sum=1e18;
for(int j=0; j<4; ++j) {
if(abs(B[j])==2e9) continue ;
sum=min(sum, 1LL*A[i]*B[j]);
}
ans=max(ans, sum);
}
cout<<ans; putchar('\n');
}
}
T3 星战( \(galaxy\) ) 2s 512M
其实把话说明白了,就是问你当前这个时候是不是所有点的入度都为 \(1\)。
一个很奇怪的思路。
用一个数去表示是否当前每一个点的入度都是 \(1\),也就是哈希一下。
那么按照每一个点的编号的平方乘上出度个数的值的和去哈希。
先算出一个标准值,即入度都为 \(1\) 的时候,哈希值应该是什么。
然后开始维护哈希值就行了。
int n, m, q, t;
int u, v;
ll tmp[500005];
ll sy[500005];
ll Sum;
ll Rit;
int main(){
n=read(); m=read();
for(int i=1; i<=n; ++i) Rit+=i*i;
for(int i=1; i<=m; ++i) {
u=read(); v=read();
sy[v]+=u*u;
tmp[v]+=u*u;
Sum+=u*u;
}
q=read();
while(q--) {
t=read();
if(t==1) {
u=read(); v=read();
sy[v]-=u*u;
Sum-=u*u;
}
if(t==2) {
u=read();
Sum-=sy[u];
sy[u]=0;
}
if(t==3) {
u=read(); v=read();
sy[v]+=u*u;
Sum+=u*u;
}
if(t==4) {
u=read();
Sum+=tmp[u]-sy[u];
sy[u]=tmp[u];
}
if(Sum==Rit) puts("YES");
else puts("NO");
}
}
T4 数据传输( \(transmit\) ) 3s 1G
这道题没做出来,挺难的,只拿到了 \(52\) 分。
对于 \(k=1\) 的情况,路径权值和板子。
前面千量级的范围,直接 Dij 跑个最短路就行了。
建边的时候稍微有点特别,但是很好想。
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
ll read(){
ll x=0;bool f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f?x:(~x)+1;
}
const ll maxn=200001;
ll n,m,q,k,rt,u,v,opt,x,y,z,ans;
ll far[maxn],dep[maxn],son[maxn],siz[maxn];
ll seg[maxn],rel[maxn],top[maxn],num[maxn];
struct EDGE{
ll t,n;
}e[maxn<<1];
ll head[maxn],head_size;
struct tree{
ll l,r;
ll sum,lzy;
}t[maxn<<2];
void ADD(ll f,ll t){
e[++head_size].t=t;
e[head_size].n=head[f];
head[f]=head_size;
}
//--------------------------------------------------------dfs预处理
void dfs1(ll x,ll fa){
++siz[x];dep[x]=dep[fa]+1;far[x]=fa;
for(ll i=head[x];i;i=e[i].n){
if(e[i].t==fa)continue;
dfs1(e[i].t,x);
siz[x]+=siz[e[i].t];
if(siz[e[i].t]>=siz[son[x]])
son[x]=e[i].t;
}
}
void dfs2(ll x,ll fa){
if(son[x]){
int v=son[x];
seg[v]=++seg[0];
rel[seg[0]]=v;
top[v]=top[x];
dfs2(v,x);
}
for(ll i=head[x];i;i=e[i].n){
if(top[e[i].t])continue ;
int v=e[i].t;
seg[v]=++seg[0];
rel[seg[0]]=v;
top[v]=v;
dfs2(v,x);
}
}
//--------------------------------------------------------线段树
void pushup(ll g){t[g].sum=t[g<<1].sum+t[g<<1|1].sum;}
void build(ll l,ll r,ll g){
if(l==r){
t[g].l=t[g].r=l;
t[g].sum=num[rel[l]];
return ;
}
ll mid=l+r>>1;
build(l,mid,g<<1);build(mid+1,r,g<<1|1);
t[g].l=l;t[g].r=r;
pushup(g);
}
ll SUM(ll g,ll x,ll y){
if(x<=t[g].l&&t[g].r<=y){
return t[g].sum;
}
ll mid=t[g].l+t[g].r>>1;ll re=0;
if(x<=mid)re+=SUM(g<<1,x,y);
if(y>mid)re+=SUM(g<<1|1,x,y);
return re;
}
ll LCASUM(ll x,ll y){
ll fx=top[x],fy=top[y];
ll re=0;
while(fx!=fy){
if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
re+=SUM(1,seg[fx],seg[x]);
x=far[fx];fx=top[x];
}
if(dep[x]>dep[y])swap(x,y);
re+=SUM(1,seg[x],seg[y]);
return re;
}
ll dis[5003][5003];
int pd[5003][5003];
struct edge{
int t, n;ll d;
}E[8000006];
int head2[200005], head_size2;
void add(int f, int t, ll d) {
E[++head_size2]=(edge) {t, head2[f], d};
head2[f]=head_size2;
}
struct node {
int v; ll diss;
bool operator < (const node &a) const {
return a.diss<diss;
}
};
priority_queue<node>Q;
void DFS(int x, int fa, int now, int dep) {
if(x!=now) add(now, x, num[x]);
if(dep==k) return ;
for(int i=head[x]; i; i=e[i].n) {
if(e[i].t==fa) continue ;
DFS(e[i].t, x, now, dep+1);
}
}
void jianbian() {
for(int i=1; i<=n; ++i) DFS(i, i, i, 0);
}
void Dij(int S) {
dis[S][S]=0;
Q.push((node){S, 0});
while(!Q.empty()) {
node op=Q.top(); Q.pop();
if(op.diss>dis[S][op.v]) continue ;
for(int i=head2[op.v]; i; i=E[i].n) {
if(dis[S][E[i].t]>dis[S][op.v]+E[i].d) {
dis[S][E[i].t]=dis[S][op.v]+E[i].d;
Q.push((node){E[i].t, dis[S][E[i].t]});
}
}
}
}
int main(){
n=read(); q=read(); k=read(); rt=1;
for(ll i=1;i<=n;i++){
num[i]=read();
}
for(ll i=1;i<n;i++){
u=read();v=read();
ADD(u,v);ADD(v,u);
}
if(k==1) {
top[rt]=rt;seg[rt]=++seg[0];rel[seg[0]]=rt;
dfs1(rt,rt); dfs2(rt,rt); build(1,n,1);
}
if(k!=1) {
jianbian();
memset(dis, 0x3f, sizeof(dis));
for(int i=1; i<=n; ++i) Dij(i);
}
while(q--) {
u=read(); v=read();
if(k==1) {
cout<<LCASUM(u, v); putchar('\n');
}
else {
cout<<num[u]+dis[u][v];
putchar('\n');
}
}
}
NOIP 也这个水平就好了。