ICPC2024shanghai

前言

铁了,唉,赛时发挥一坨屎

I. In Search of the Ultimate Artifact

赛时一眼盯真,鉴定为纯纯的暴力,自己写了会发现老是不对,让队长上机调了几分钟过了

题意即每次选当前最大的 $k$ 个数相乘,求最后能得到的最大的数是多少,对这个最大的数取模

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
#define ls now<<1
#define rs now<<1|1
#define endl "\n"
#define lowbit(x) ((x)&(-x))
typedef long long ll;
const int N=2e5+7, mod=998244353;

int n,k;
ll a[N];

void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+1+n,greater<ll>());
	ll ans=a[1]%mod;
	deque<int>q;
	ll tmp=1;
	for(int i=2;i<=n;i++){
		if(a[i]==0)break;
		q.push_back(a[i]);
		tmp=(tmp*a[i])%mod;
		if((int)q.size()%(k-1)==0){
			ans=(ans*tmp)%mod;
			tmp=1;
		}
	}
	cout<<ans<<endl;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t=1;
	cin>>t;
	while(t--)solve();
	return 0;
}

C. Conquer the Multiples

赛时居然卡了 1 个多小时

简单的博弈论

易发现,偶数乘于任何数都还是偶数,即从偶数开始的人,是无法阻碍另一个人的,但是奇数的人可以去拿偶数位上的,即奇数开始的人处于优势地位

基础情况为每个人只拿当前位置

最优情况,偶数仍然只能拿当前位置,拿后面的只会妨碍自己,奇数能拿偶数时优先拿偶数

可以发现,基础步数是区间内奇偶的个数,考虑到最优情况,奇数会拿走偶数的,我们只需要看奇数按最优的操作后,谁的步数多即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <bits/stdc++.h>
using namespace std;
#define ls now<<1
#define rs now<<1|1
#define endl "\n"
#define lowbit(x) ((x)&(-x))
typedef long long ll;
const int N=2e5+7, mod=998244353;

ll l,r;

void solve(){
	cin>>l>>r;
	if(l&1){
		if(l*2<=r){cout<<"Alice\n";return;}
	}
	else {
		if((l+1)*2<=r){cout<<"Bob\n";return;}
	}
	cout<<(((r-l+1)&1) ? "Alice\n" : "Bob\n");
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t=1;
	cin>>t;
	while(t--)solve();
	return 0;
}

G. Geometry Task

赛场上太急了,思路只对了一部分,还有部分没想到就上机开始硬调了

显然易想到二分答案,重点就是 $check$ 的过程

开始想法是对于每个 $红线$,用二分找他的最优的 $蓝线$ 与其匹配,但是忽视了 $a<0$ $a>0$ 对于最优的要求的不同 例如 $2$ 者的最优都是 $1$ 时,$蓝线$ 数据为 ${1,2,3}$,如果先判断了正的那个,它就会取走 $1$,那么 负的就取不了了

所以,将问题转化为区间取数问题,对于负数,是在 $[1,x]$,上取数,对于正数,是在 $[x,n]$ 上取数
将正负分开处理,分别考虑能取走几个即可,最后返回 $cnt>=(mid+1)/2$ 即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=2e5+7;

struct Node{
	ll a,b;
	bool operator<(const Node &tmp){
		if(a==tmp.a)return b<tmp.b;
		return a<tmp.a;
	}
};

Node a[N];
ll c[N];
int cnt[N];
int n,fu,zheng;

bool check(ll x){
	int res=0;
	for(int i=1;i<=n;i++)cnt[i]=0;
	for(int i=1;i<=n;i++){
		if(a[i].a<0){
			ll l=1,r=fu,ans=0;
			while(l<=r){
				ll mid=(l+r)>>1;
				if(a[i].a*c[mid]+a[i].b>=x)l=mid+1,ans=mid;
				else r=mid-1;
			}
			cnt[ans]++;
		}
		else if(a[i].a==0){
			if(a[i].b>=x)res++;
		}
		else{
			ll l=zheng,r=n,ans=0;
			while(l<=r){
				ll mid=(l+r)>>1;
				if(a[i].a*c[mid]+a[i].b>=x)r=mid-1,ans=mid;
				else l=mid+1;
			}
			cnt[ans]++;
		}
	}
	int sum=0;
	for(int i=1;i<=fu;i++){
		sum++;
		if(cnt[i]){
			if(sum>=cnt[i])res=res+cnt[i],sum=sum-cnt[i];
			else res=res+sum,sum=0;
		}
	}
	sum=0;
	for(int i=n;i>=zheng;i--){
		sum++;
		if(cnt[i]){
			if(sum>=cnt[i])res+=cnt[i],sum-=cnt[i];
			else res+=sum,sum=0;
		}
	}
	return res>=(n+1)/2;
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].a;
	for(int i=1;i<=n;i++)cin>>a[i].b;
	for(int i=1;i<=n;i++)cin>>c[i];
	sort(a+1,a+1+n);
	sort(c+1,c+1+n);
	ll l=-2e18,r=2e18,ans=0;
	fu=0,zheng=n+1;
	for(int i=n;i>0;i--){
		if(a[i].a<0){
			fu=i;
			break;
		}
	}
	for(int i=1;i<=n;i++){
		if(a[i].a>0){
			zheng=i;
			break;
		}
	}
	while(l<=r){
		ll mid=ll(l+r)>>1;
		if(check(mid))l=mid+1,ans=mid;
		else r=mid-1;
	}
	cout<<ans<<endl;
}

int main(){
	int t;
	cin>>t;
	while(t--)solve();
	return 0;
}

B. Basic Graph Algorithm

md 三个人硬是没开出来

显然最优是连不到下个点时加边,前面能回溯时则不加,选择回溯来实现这个点

加边一定是加到靠前的点更优

代码实现:

  • 首先开一个超级源点 $0$ 可连到所有的节点,同时所有的节点也能回到这个超级源点

  • 然后记录条边连接的点

  • 用 $dfs$ 实现,我们从 $0$ 端点开始,将能到达的点中回来的路全部删掉,接着判断是否子节点中是否能够通往下一个节点,如果没有对应的路,就加点,接着以下一个点进行 $dfs$

  • 实现回溯的关键在于 $dfs$ 中的判断采用的是 $while$,如果当前这个节点他没有边,那我们应该回去,此时之前的节点仍然能进行判断,是否需要加边

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
using namespace std;
#define ls now<<1
#define rs now<<1|1
#define endl "\n"
#define lowbit(x) ((x)&(-x))
typedef long long ll;
const int N=3e5+7, mod=1e9+7;

int n,m,k;
int p[N];
int vis[N];
vector<pair<int,int>>ans;
vector<int>edges[N];
set<int>edge[N];

void dfs(int now){
	k++;
	vis[now]=1;
	for(int v:edges[now])edge[v].erase(now);
	while(edge[now].size()&&k<=n){
		if(!edge[now].count(p[k]))ans.push_back({now,p[k]});
		dfs(p[k]);
	}
}

void solve(){
	cin>>n>>m;
	ans.clear();
	k=0;
	for(int i=1;i<=n;i++){
		edge[i].insert(0);
		edge[0].insert(i);
		edges[i].push_back(0);
		edges[0].push_back(i);
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		edge[u].insert(v);
		edge[v].insert(u);
		edges[u].push_back(v);
		edges[v].push_back(u);
	}
	for(int i=1;i<=n;i++)cin>>p[i];
	dfs(0);
	cout<<ans.size()<<endl;
	for(auto [x,y]:ans)cout<<x<<" "<<y<<endl;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t=1;
	// cin>>t;
	while(t--)solve();
	return 0;
}

D. Decrease and Swap

开始队友读错题,以为每个盒子里一块石头,差点上机特判,还好冷静了,虽然对结果也没什么影响

实际上。每个盒子内都是无限块石头,那考虑时,可以考虑某个区间内的整体操作,即我们遍历向后扫,记录当前各出现的 $01$ 次数,如果 $cnt0>cnt1$ 那么,经过神奇的变换操作,我们一定可以将这个区间内所有的 $1$ 变为 $0$,最后如果 $cnt1+cnt0>3 || cnt1+cnt0==0$ 那么就是 Yes

为什么 $cnt0>cnt1$ 当前区间一定能转化成功:

  • 因为我们的操作次数是无限的,同时这个触发条件一定有两个 $0$ ,那么我们经过一些交换后,总能使两个 $0$ 到达当前串的末尾,此时前面的盒子直接无脑消掉即可

那么最后 $cnt1+cnt0>3$ 为什么是 Yes:

  • 最后剩下的没转化的串中,$1$ 的个数大于等于 $0$ 的个数,同时这个串的长度大于 $3$,即我们可以先对几个 $1$ 操作,转化出来两个 $0$,再根据前面的理论,此时的串一定能转换过去
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
#define ls now<<1
#define rs now<<1|1
#define endl "\n"
#define lowbit(x) ((x)&(-x))
typedef long long ll;
const int N=1e6+7, mod=1e9+7;

int n;
string s;
int cnt[N];

void solve(){
	cin>>n>>s;
	int c1=0,c0=0;
	for(int i=0;i<n;i++){
		if(s[i]=='1')c1++;
		else c0++;
		if(c0>c1)c1=c0=0;
	}
	if(c1+c0>3||c1+c0==0){
		cout<<"Yes\n";
	}
	else cout<<"No\n";
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t=1;
	cin>>t;
	while(t--)solve();
	return 0;
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Sep 11, 2025 16:27 +0800
发表了95篇文章 · 总计15万2千字
永远相信美好的事情即将发生。
使用 Hugo 构建
主题 StackJimmy 设计