2020 ICPC 上海站

本文详细分析了四道编程竞赛题目:扫雷优化、对数和计算、行走者策略及斐波那契数列问题。通过转换思维,利用图的补图、数位DP和分情况讨论等方法解决复杂问题,同时展示了AC自动机和持久化trie树在实际问题中的应用。

B : M i n e S w e e p e r I I \mathbf{B:Mine Sweeper II} B:MineSweeperII

题目大意

给你两个大小为 n ∗ m n * m nm 的扫雷图 A , B A, B A,B,要求通过最多 n ∗ m 2 \frac{n * m}{2} 2nm次转换(转换指 把一个不是雷的格子变成雷, 或者把一个是雷的格子变成空白格),使得 B图的值 和 A图的值相等(值 就是指 扫雷图上的所有数字之和,没有数字或者雷的地方的值为 0 0 0

分析

一个地雷对周围的影响,等价于把这个地雷改成空白,把周围的空白改成地雷,所以我们只需要求这个图的补图就可以了

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC option("arch=native","tune=native","no-zero-upper")
#pragma GCC target("avx2")
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e3 + 10;
char a[N][N];
char b[N][N];
int n,m;

int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)
        scanf("%s",a[i] + 1);
    for(int i = 1;i <= n;i++)
        scanf("%s",b[i] + 1);
    int ans = 0;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            if(a[i][j] != b[i][j]) ans++;
    if(ans <= n * m / 2){
        for(int i = 1;i <= n;i++)
            printf("%s\n",a[i] + 1);
    }
    else{
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= m;j++)
                if(a[i][j] == '.') printf("X");
                else printf(".");
            puts("");
        }
    }
    return 0;
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/

C : S u m o f L o g \mathbf{C:Sum of Log} C:SumofLog

题目大意

给定 x , y x,y x,y,我们需要把在这里插入图片描述
给算出来

分析

这个问题可以转换成,一个范围内,当 i & j = = 0 i \& j == 0 i&j==0 的的情况下,二进制最高位的所有数的和,所以我们可以想到数位DP的做法
我们可以分两种情况来讨论,一种情况是当 i > j i > j i>j 的情况,这个时候 log ⁡ 2 ( i + j ) \log_2(i + j) log2(i+j)的值就是 i i i的二进制表示下最高位的位置,反过来再讨论 i < j i < j i<j 的情况即可

代码

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
#define _CRT_SECURE_NO_WARNINGS
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const int N = 50;
const ll mod= 1000000007;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
int gcd(int a,int b){return (b>0)?gcd(b,a%b):a;}
ll f[N][2][2];
ll x,y;
int a[N],b[N];

ll dfs(int pos,bool sta,bool stb){
    if(!pos) return 1;
    if(f[pos][sta][stb] != -1) return f[pos][sta][stb];
    int ed1 = sta ? a[pos] : 1;
    int ed2 = stb ? b[pos] : 1;
    ll res = 0;
    for(int i = 0;i <= ed1;i++)
        for(int j = 0;j <= ed2;j++){
            if(j & i) continue;
            res = (res + dfs(pos - 1,sta && i == ed1,stb && j == ed2));
        }
    f[pos][sta][stb] = res;
    return res;
}

ll run(){
    int la = 0,lb = 0;
    memset(a,0,sizeof a);
    memset(b,0,sizeof b);
    while(x) {
        a[++la] = x & 1;
        x >>= 1;
    }
    while(y){
        b[++lb] = y & 1;
        y >>= 1;
    }
    ll res = 0;
    memset(f,-1,sizeof f);
    for(int i = la;i;i--){
        res = (res + dfs(i - 1,i == la,i > lb) * i) % mod;
    }
    memset(f,-1,sizeof f);
    for(int i = lb;i;i--){
        res = (res + dfs(i - 1,i > la,i == lb) * i) % mod;
    }
    return res;
}

int main(){
    int T;
    read(T);
    while(T--){
        read(x),read(y);
        dl(run());
    }
    return 0;
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/



D : W a l k e r \mathbf{D:Walker} D:Walker

题目大意

给你两个人的位置和速度,以及这段路程的总长度,问你最少多长时间两个人可以走完这整段路程

分析

分情况讨论即可
1. 1. 1.第一个人独自走完全程
2. 2. 2.第二个人独自走完全程
3. 3. 3.右边的人向左走左边的人向右走
4. 4. 4.最终在中间相遇,二分位置

代码

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
#define _CRT_SECURE_NO_WARNINGS
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const ll mod= 1000000007;
const double eps = 1e-10;
const double PI = acos(-1);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
int gcd(int a,int b){return (b>0)?gcd(b,a%b):a;}
double v1,v2,p1,p2,n;
double res4;
int sgn(double x)
{
    if(fabs(x)<=eps)
        return 0;
    else if(x<0)
        return -1;
    else
        return 1;
}

bool check(double mid){
    double t1 = (min(mid - p1,p1) + mid)/ v1;
    double t2 = (min(p2 - mid,n - p2) + n - mid) / v2;
    res4 = min(res4,max(t1,t2));
    return sgn(t1 - t2) < 0;
}

int main(){
    int T;
    read(T);
    while(T--){
        cin >> n >> p1 >> v1 >> p2 >> v2;
        if(p1 > p2) swap(p1,p2),swap(v1,v2);
        double res1 = (min(n - p1,p1) + n) / v1;
        double res2 = (min(n - p2,p2) + n) / v2;
        double res3 = max((n - p1) / v1,p2 / v2);
        res4 = 1e9;
        double l = p1,r = p2;
        for(int i = 1;i <= 1000;i++){
            double mid = (l + r) / 2;
            if(check(mid)) l = mid;
            else r = mid;
        }
        printf("%.10lf\n",min(min(res1,res2),min(res3,res4)));
    }
    return 0;
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/



G : F i b o n a c c i \mathbf{G:Fibonacci} G:Fibonacci

题目大意

将当前斐波那契数分别与其后到 n n n的数相乘,值为偶数总和加一。

分析

签到题,找一下规律就可以了

代码

#include <iostream>

using namespace std;
typedef long long ll;

int main(){
	ll sum = 0;
	ll n;
	scanf("%lld",&n);
	sum = n / 3 * (n - 1) - (n / 3) * (n / 3 - 1) / 2;
	printf("%lld",sum);
}

M : G i t i g n o r e \mathbf{M:Gitignore} M:Gitignore

题目大意

给你一些文件夹和文件,文件夹没有空的,文件夹里面可能有文件和文件夹,有些文件是可以忽略的,有些文件是不能被忽略的,我们要找到最少的能被忽略的文件的个数。

分析

几个月后再看这道题发现,小丑竟是我自己
当时没学 a c ac ac自动机,可持久化 t r i e trie trie也是直接跳过直接学的主席树,导致差点都忘了字典树这个数据结构,最后用一种很复杂的存图方式把这棵树存下来,然后 d f s dfs dfs一下就可以了
来欣赏一下小丑的代码

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
map<string,int> M;
int h[N],ne[N],e[N],idx;
int ppp[N][N];
int sum[N];
int son[N];
bool st[N];
int ans;
int num;
string str;
int n,m;

int find(string str){
	if(!M[str]) M[str] = ++num;
	return M[str];
}

void add(int x,int y){
	ne[idx] = h[x],e[idx] = y,h[x] = idx++;
}

void init(){
	memset(h,-1,sizeof h);
	memset(ppp,0,sizeof ppp);
	memset(sum,0,sizeof sum);
	memset(son,0,sizeof son);
	memset(st,0,sizeof st);
	M.clear();
	idx = 0,num = 0;
	ans = 0;
	scanf("%d%d",&n,&m);
}

void dfs(int u){
	if(h[u] == -1) son[u] = 1;
	for(int i = h[u];~i;i = ne[i]){
		int j = e[i];
		dfs(j);
		son[u] += son[j];
		sum[u] += sum[j];
	}
}

void qqqq(int u){
	// cout <<u << endl << ' '  <<son[u] << ' ' << sum[u] << endl;
	if(son[u] == sum[u]){
		// cout <<u << endl;
		// cout <<son[u] << ' ' << sum[u] << endl;
		ans++;
		return ;
	}
	for(int i = h[u];~i;i = ne[i]){
		int j = e[i];
		qqqq(j);
	}
}

int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		init();
		int res = 0;
		for(int i = 1;i <= n;i++){
			cin >> str;
			string back = "";
			int last = 0;
			for(int j = 0;j < str.length();j++){
				if(str[j] == '/'){
					int xx = find(back);
					if(!ppp[last][xx]){
						ppp[last][xx] = ++res;
						add(last,ppp[last][xx]);
					}
					// cout <<last <<' ' <<ppp[last][xx] << endl;
					last = ppp[last][xx];
					back = "";
				}
				else{
					back = back + str[j];
				}
			}
			int xx = find(back);
			if(!ppp[last][xx]){
				ppp[last][xx] = ++res;
				add(last,ppp[last][xx]);
			}
			// cout <<last <<' ' <<ppp[last][xx] << endl;
			sum[ppp[last][xx]] = 1;
			back = "";
		}
		for(int i = 1;i <= m;i++){
			cin >> str;
			string back = "";
			int last = 0;
			for(int j = 0;j < str.length();j++){
				if(str[j] == '/'){
					int xx = find(back);
					if(!ppp[last][xx]){
						ppp[last][xx] = ++res;
						add(last,ppp[last][xx]);
					}
					// cout <<last <<' ' <<ppp[last][xx] << endl;
					last = ppp[last][xx];
					back = "";
				}
				else{
					back = back + str[j];
				}
			}
			int xx = find(back);
			if(!ppp[last][xx]){
				ppp[last][xx] = ++res;
				add(last,ppp[last][xx]);
			}
			// cout <<last <<' ' <<ppp[last][xx] << endl;

			back = "";
		}
		dfs(0);
		for(int i = h[0];~i;i = ne[i]){
			int j = e[i];
			if(st[j]) continue;
			st[j] = true;
			qqqq(j);
		}
		printf("%d\n",ans);
	}
}


### ICPC 上海区域赛中文信息及相关详情 #### 比赛简介 国际大学生程序设计竞赛(International Collegiate Programming Contest, ICPC)是一项全球性的编程比赛,旨在通过解决复杂的算法问题来评估学生的逻辑思维能力和团队协作精神。ICPC上海区域赛是中国赛区的重要组成部分之一。 #### 历史回顾与经验分享 在过去几年中,ICPC上海区域赛吸引了众多高校参与。例如,在2019年的比赛中,有参赛者提到该赛事不仅是一次技术上的挑战,同时也提供了良好的交流机会[^3]。而在2020年的比赛中,则出现了因细节错误而导致的罚时情况,这提醒选手们在比赛中需格外注意代码质量以及测试用例覆盖范围[^4]。 #### 时间安排 具体的时间表会每年有所调整,但通常情况下,ICPC上海区域赛会在秋季举行。根据最近两年的日程来看,预计未来几年的比赛时间仍将在这一时间段内展开[^1]。确切日期一般由官方提前数月公布于其网站上,请定期访问相关链接获取最新动态。 #### 地点设置 比赛场地设在上海某所大学校园内部或者周边会议中心等地。实际位置取决于当年主办方的选择,并且可能会随着合作院校的变化而改变。因此建议关注ICPC北京总部及本地分部发布的公告以获得最精确的信息[^1]。 #### 参赛资格与队伍构成 任何全日制在校本科生均有资格报名参加此项活动;每支代表队限三人组成,成员之间应具备良好沟通技巧并能高效分工完成任务目标。需要注意的是,部分特殊规则可能涉及往届成绩考量等因素,所以务必仔细阅读当年度指南说明文件后再做决定是否组建新团队或是沿用旧阵容继续冲击更高荣誉等级[^2]。 ```python # 示例:如何查询最新的ICPC上海站信息? import requests def fetch_icpc_shanghai_info(): url = "https://icpc.baylor.edu/" # 替换为具体的上海站点URL response = requests.get(url) if response.status_code == 200: print("成功获取ICPC上海区域赛信息") return response.text[:50] + "..." # 返回前50字符作为示例展示 else: raise Exception(f"无法连接至服务器,状态码:{response.status_code}") fetch_icpc_shanghai_info() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值