首页 > 其他分享 >CSP-S2022

CSP-S2022

时间:2022-11-19 17:26:17浏览次数:51  
标签:return int sum long fa S2022 CSP dis

勉强混到 CQ 一等但是差 \(5\) 分 \(7\) 级勾(哭)。

A. 假期计划

我们先不考虑 \(4\) 个点,考虑 \(2\) 个点的情况。

我们发现可以枚举 \(a\) 点,再找到 \(a\) 能到达且 \(1\) 能到达的最大权值的点 \(b\)。

回到该题,运用刚才的思想,我们便可以通过枚举 \(b,c\) 点,按上述操作得到 \(a,d\) 点。即在处理 BFS 时提前处理好该点能到且 \(1\) 点能到的权值前 \(3\) 大的点。(为什么要取 \(3\) 个,为了防止枚举 \(b\) 时 \(a,c,d\) 重复)。

#include<bits/stdc++.h>
using namespace std;
const int N=2510;
vector<int> l[N];
void add(int u,int v){
	l[u].push_back(v);
	return ;
}
long long w[N];
vector<long long> sum[N];
bool go[N][N];
int dis[N];
int k,n;
void bfs(int s){
	for(int i=1;i<=n;i++){
		dis[i]=INT_MAX;
	} 
	dis[s]=0;
	queue<int> q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		if(dis[u]>k){
			continue;
		}
		if(u!=s){
			go[s][u]=1;
			if(u>1&&go[1][u]){
				sum[s].push_back(u);
				sort(sum[s].begin(),sum[s].end(),[](int x,int y){
					return w[x]>w[y];
				});
				if(sum[s].size()==4){
					sum[s].pop_back();
				}//处理出最大的3个
			}
		}
		for(auto v:l[u]){
			if(dis[u]+1<dis[v]){
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return ;
}
int main(){
	int m;
	scanf("%d%d%d",&n,&m,&k);
	k++;
	for(int i=2;i<=n;i++){
		scanf("%lld",&w[i]);
	}
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++){
		bfs(i);
	}
	long long ans=0;
	for(int b=2;b<=n;b++){
		for(int c=2;c<=n;c++){//枚举b,c
			if(go[b][c]){
				for(auto a:sum[b]){
					if(a==c){
						continue;
					}
					for(auto d:sum[c]){
						if(d==b||d==a){
							continue;
						}
						ans=max(ans,w[a]+w[b]+w[c]+w[d]);
					}
				}
			}
		}
	}
	printf("%lld",ans);
	return 0;
}

B. 策略游戏

可以发现,答案即为对于 \(i=l1\sim r1\) 行中 \(\min\{c_{i,j}\}(j=l2\sim r2)\) 最大的一行的最小值。

我们发现有多种情况:

  1. 若 \(a_i\ge0\),则另一方会选 \(\min\{b_j\}\)。当然,若 \(\min\{b_j\}\ge0\),会选 \(\max\{a_i\}\);若 \(\min\{b_j\}<0\),会选 \(\min\{a_i\}(a_i\ge0)\)。
  2. 若 \(a_i<0\),则另一方会选 \(\max\{b_j\}\)。同理,若 \(\max\{b_j\}< 0\),会选 \(\min\{a_i\}\);若 \(\max\{b_j\}\ge0\),则会选 \(\max\{a_i\}(a_i<0)\)。

ST 表维护上述提到的 6 种即可。

#include<bits/stdc++.h>
using namespace std;
const int M=100010,N=100010,T=17;
long long a[N],b[M];
long long amax[N][T],amin[N][T],bmax[M][T],bmin[M][T],afmax[M][T],azmin[M][T];
int lo2[N];
int main(){
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=2;i<=max(n,m);i++){
        lo2[i]=lo2[i>>1]+1;
    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%lld",&b[i]);
    }
    for(int i=1;i<=n;i++){
        amax[i][0]=a[i];
        amin[i][0]=a[i];
        if(a[i]){
            afmax[i][0]=(a[i]<0?a[i]:INT_MIN);
            azmin[i][0]=(a[i]>=0?a[i]:INT_MAX);         
        }
        else{
            afmax[i][0]=azmin[i][0]=0;
        }
    }
    for(int len=1;(1<<len)<=n;len++){
        for(int j=(1<<len);j<=n;j++){
            amax[j][len]=max(amax[j][len-1],amax[j-(1<<(len-1))][len-1]);
            amin[j][len]=min(amin[j][len-1],amin[j-(1<<(len-1))][len-1]);
            afmax[j][len]=max(afmax[j][len-1],afmax[j-(1<<(len-1))][len-1]);
            azmin[j][len]=min(azmin[j][len-1],azmin[j-(1<<(len-1))][len-1]);
        }
    }
    for(int i=1;i<=m;i++){
        bmax[i][0]=b[i];
        bmin[i][0]=b[i];
    }
    for(int len=1;(1<<len)<=m;len++){
        for(int j=(1<<len);j<=m;j++){
            bmax[j][len]=max(bmax[j][len-1],bmax[j-(1<<(len-1))][len-1]);
            bmin[j][len]=min(bmin[j][len-1],bmin[j-(1<<(len-1))][len-1]);
        }
    }
    while(q--){
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        int Alen=lo2[r1-l1+1],Blen=lo2[r2-l2+1];
        long long Amax=max(amax[r1][Alen],amax[l1+(1<<Alen)-1][Alen]);
        long long AFmax=max(afmax[r1][Alen],afmax[l1+(1<<Alen)-1][Alen]);
        long long Bmax=max(bmax[r2][Blen],bmax[l2+(1<<Blen)-1][Blen]);
        long long Amin=min(amin[r1][Alen],amin[l1+(1<<Alen)-1][Alen]);
        long long AZmin=min(azmin[r1][Alen],azmin[l1+(1<<Alen)-1][Alen]);
        long long Bmin=min(bmin[r2][Blen],bmin[l2+(1<<Blen)-1][Blen]);
        long long ans=0;        
        if(l1==r1){
            ans=min(Amax*Bmax,Amax*Bmin);   
        }
        else if(l2==r2){
            ans=max(Amin*Bmax,Amax*Bmax);
        }
        else{
            if(Bmin>=0){
                if(Amax<0){
                    ans=Amax*Bmax;
                }
                else{
                    ans=Amax*Bmin;
                }
            }
            else if(Bmax<=0){
                if(Amin<0){
                    ans=Amin*Bmax;
                }
                else{
                    ans=Amin*Bmin;
                }
            }
            else{
                ans=max(AZmin*Bmin,AFmax*Bmax);
            }           
        }
        printf("%lld\n",ans);
    }
    return 0;
}

C. 星战

“不可以,总司令。”
个人认为本次 S 最简单的题。

手模一下发现只要满足 2 性质,即每个点有且仅有一条出边即能“反攻”。

于是我们可以把 \(u\to v\) 的边转化为 \(v\to u\)(之后出现的 \(u\to v\) 默认以转换),给每个点定义一个哈希值 \(hsh_i\)。

我们令 \(all_v=\sum\limits_{u\to v} hsh_u\),即一开始没有边被摧毁时点应有的哈希值。

令 \(now_v\) 为实时变换的该点的哈希值,初值即为 \(all_v\)。

若删除(增加)一条边,则为 \(now_v-(+)hsh_u\)。
若摧毁(修复)一条边,则为 \(now_v = 0(all_v)\)。

判断 \(\sum\limits_{i=1}^n now_i\) 是否等于 \(\sum\limits_{i=1}^n hsh_i\) 即可。可以用变量记录实时更新。

#include<bits/stdc++.h>
using namespace std;
const int N=500100;
const unsigned long long p=1000003;//要超过N的范围
unsigned long long sum,ac,all[N],now[N],s[N];//自然溢出
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	s[1]=1;
	ac=1;
	for(int i=2;i<=n;i++){
		s[i]=s[i-1]*p;
		ac+=s[i];
	}
	while(m--){
		int u,v;
		scanf("%d%d",&u,&v);
		sum+=s[u];
		all[v]+=s[u];
		now[v]+=s[u];
	}
	int q;
	scanf("%d",&q);
	while(q--){
		int opt;
		scanf("%d",&opt);
		if(opt==1){
			int u,v;
			scanf("%d%d",&u,&v);
			now[v]-=s[u];
			sum-=s[u];
		}
		else if(opt==2){
			int v;
			scanf("%d",&v);
			sum-=now[v];
			now[v]=0;
		}
		else if(opt==3){
			int u,v;
			scanf("%d%d",&u,&v);
			now[v]+=s[u];
			sum+=s[u];
		}
		else{
			int v;
			scanf("%d",&v);
			sum+=(all[v]-now[v]);
			now[v]=all[v];
		}
		if(sum==ac){
			printf("YES");
		}
		else{
			printf("NO");
		}
		printf("\n");
	}
	return 0;
}

D. 数据传输

首先可以把询问的条件转化为中间连续不经过的点不超过 \(k\) 个。

则令 \((u,x)\) 双元组为在 \(u\) 点,已有 \(x\) 个连续点没经过。

但这样暴力跑时间复杂度为 \(O(qn\log_2n)\),很不理想。
我们便考虑利用倍增求 LCA 一步步往上维护信息。

令 \(dp_{u,i,x,y}\) 为 \((u,x)\) 到 \((fa_u^{2^i},y)\) 的最短距离。

那我们便可以像倍增 LCA 一样预处理 \(dp_{u,0,x,y}\),之后便利用 \(dp_{u,i-1,x,z},dp_{fa_u^{2^{i-1}},i-1,z,y}\) 转移即可。发现有 \(3\) 种情况:

  1. 走到 \(fa_u^1\)。
  2. \(u,fa_u^1\) 都不走。
  3. 走到与 \(u\) 有连边且权值最小的点再走到 \(fa_u^1\)。

答案便可以在向上求 LCA 时转移。但要注意若 \(x=y=0\) 时多算了一次 LCA 的权值。若 \(x+y>k\) 则说明还到不了 LCA,还需要找一个与 LCA 有连边的权值最小的点做转移。

#include <bits/stdc++.h>
using namespace std;
void Min (long long & u, long long v) {
    u = min (u, v);
    return ;
}
const int N = 200100;
vector <int> l [N];
long long v [N], near [N];
void add (int x, int y){
    l [x] .push_back (y);
    Min (near [x], v [y]);
    return ;
}
const int M = 18, K = 3;
int depth [N], fa [N] [M];
long long dis [N] [M] [K] [K];
int s;
void Dfs (int u, int fat) {
    depth [u] = depth [fat] + 1;
    fa [u] [0] = fat;
    auto t = dis [u] [0];
    if (s == 1) {
        t [0] [0] = v [fat];
    }
    else if (s == 2) {
        t [0] [0] = t [1] [0] = v [fat];
        t [0] [1] = 0;
    }
    else {
        t [0] [0] = t [1] [0] = t [2] [0] = v [fat];
        t [0] [1] = t [1] [2] = 0;
        t [2] [2] = near [u];
    }//初始化
    for (int i = 1; i < M; i ++) {
        fa [u] [i] = fa [fa [u] [i - 1]] [i - 1];
        for (int j = 0; j < K; j ++) {
            for (int k = 0; k < K; k ++) {
                for (int h = 0; h < K; h ++) {
                    Min (dis [u] [i] [j] [h], dis [u] [i - 1] [j] [k] + dis [fa [u] [i - 1]] [i - 1] [k] [h]);
                }
            }
        }//找中间值转移
    }
    for (auto to : l [u]) {
        if (to == fat) {
            continue;
        }
        Dfs (to, u);
    }
    return ;
}
int LCA (int x, int y) {
    if (depth [x] < depth [y]) {
        swap (x, y);
    }
    for (int i = M - 1; i >= 0; i --) {
        if (depth [fa [x] [i]] >= depth [y]) {
            x = fa [x] [i];
        }
    }
    if (x == y) {
        return x;
    }
    for (int i = M - 1; i >= 0; i --) {
        if (fa [x] [i] != fa [y] [i]) {
            x = fa [x] [i];
            y = fa [y] [i];
        }
    }
    return fa [x] [0];
}//模板
long long X [K], Y [K];
void Update (int u, int fat, long long *U) {
    U [0] = U [1] = U [2] = v [u];
    for (int i = M - 1; i >= 0; i --) {
        if (depth [fa [u] [i]] >= depth [fat]) {
            long long P [3] = {U [0], U [1], U [2]};
            U [0] = U [1] = U [2] = 1e18;
            for (int j = 0; j < K; j ++) {
                for (int k = 0; k < K; k ++) {
                    Min (U [k], P [j] + dis [u] [i] [j] [k]);
                }
            }//找中间值转移
            u = fa [u] [i];
        }
    }
    return ;
}
long long Ans (int x, int y) {//为了方便调试拆开了求LCA和转移
    int lca = LCA (x, y);
    Update (x, lca, X);
    Update (y, lca, Y);
    long long ans = 1e18;
    for (int i = 0; i < s; i ++) {
        for (int j = 0; j < s; j ++) {
            long long w = 0;
            if (i + j == 0) {//多算了一次
                w = - v [lca];
            }
            if (i + j > s) {//要再找一个
                w = near [lca];
            }
            Min (ans, X [i] + Y [j] + w);
        }
    }
    return ans;
}
signed main () {
    ios :: sync_with_stdio (false);
    cin .tie (nullptr);
    cout .tie (nullptr);
    int n, q;
    cin >> n >> q >> s;
    for (int i = 1; i <= n; i ++) {
        cin >> v [i];
        near [i] = v [i];
    }
    for (int i = 1; i < n; i ++) {
        int x, y;
        cin >> x >> y;
        add (x, y);
        add (y, x);
    }
    memset (dis, 1, sizeof (dis));//注意其余的要设为INF
    Dfs (1, 0);
    while (q --) {
        int x, y;
        cin >> x >> y;
        cout << Ans (x, y) << '\n';
    }
    return 0;
}

标签:return,int,sum,long,fa,S2022,CSP,dis
From: https://www.cnblogs.com/lhzawa/p/16906522.html

相关文章

  • 2022csp普及组真题:解密(decode)
    2022csp普及组真题:解密(decode)题目【题目描述】给定一个正整数 k,有 k 次询问,每次给定三个正整数 ni,ei,di,求两个正整数 pi,qi, 使 ni=pi×qi, ei×......
  • 第五十一章 开发自定义标签 - 使用%CSP.Rule方法
    第五十一章开发自定义标签-使用%CSP.Rule方法类中的%CSP.Rule包含几个可在<csr>规则定义中使用的实例方法。这些方法可以是两种类型之一:只读并返回元素值的方法......
  • CSP-J2022
    D整活导致\(400\)变\(390\)哭。A.乘方其实枚举就能过,特判\(a=1\)就行了。但是考场上看这题太像快速幂了就码了个快速幂。普通的快速幂:(longlong)tot=1;whi......
  • 关于VS2022 报错:MSB3027 无法将"xxx.dll"复制到"xxx"超出了重试计数 10 问题分析与解
    从标题我们不难看出,这个问题实际就是系统不能够将这个dll文件那过来放到其他目录去。其实我不知道大家是一个什么思路哈,我一开始就是无脑的去网上查找解决方案,但是在网上......
  • CSP 202206-1 归一化处理 C++
    1#include<iostream>2#include<vector>3#include<math.h>45intmain(){6intx{},sum{};7std::cin>>x;8std::vector<int>n(x,0);......
  • CSP 201403-1 相反数 C++
    1#include<iostream>2#include<vector>3#include<algorithm>45intmain(){6intx{},sum{};7std::cin>>x;8std::vector<int>n(......
  • CSP 201312-2 ISBN号码 C++
     1#include<iostream>2#include<algorithm>4#include<array>56intmain(){7std::array<int,9>ISBN{};8charc{};9intlenth{......
  • CSP-J 逻辑表达式
    对于a&b这种形式,如果是0&,则b的值不重要,发生依次短路,新的额结构体的短路次数=a的短路次数+1如果是1&,新的结构体的短路次数=a和b的短路次数之和,把新的结构体压入栈中#incl......
  • P8816 [CSP-J 2022] 上升点列 题解
    题目传送门:luoguP8816[CSP-J2022]上升点列这是一道简单的dp题。定义到达指两点的曼哈顿距离是\(1\)。我们考虑设状态\(f(i,j)\)表示考虑结尾是第\(i\)个点,增......
  • CSP-S退役记2
    今天三调,不过和我们没有关系,毕竟也不参考。终于可以考一次年级倒第一了。中午403的化奥大佬说我们宿舍被通报没有值日,苗要在考完试后停他们两位的课,他觉得停不了我们的课......