AT_abc413

又是战犯的一周啊。

再见宣言真好听。

A.Content Too Large

translation:

判断是否有 \(\sum_{i=1}^n A_i\le M\)

无脑题,不给代码了。赛时除了手速有点慢以外没啥别的问题。

B.cat 2

translation:

给定 \(n\) 个字符串 \(S_1,S_2,\dots,S_n\),任选两个不同的字符串前后连接可以得到 \(n(n-1)\) 个字符串。求这 \(n(n-1)\) 个字符串中有多少个不同的字符串。

\(n\le 100\),所以对照题面逐句实现即可,拿个 set 去下重就没有然后了。不给代码。

C.Large Queue

translation:

有一个队列,初始为空。有 \(q\) 次操作,如下:
1 c x:在队列末尾插入 \(c\)\(x\)
2 k:弹出队首的 \(k\) 个数,并输出他们的和。

逐句实现显然不现实。注意到对于某个数 \(x\),如果我们知道了它在需要弹出的数中出现的次数,那么可以 \(O(1)\) 求贡献。于是记录每个数插入后的结尾位置,每次相当于查询一段区间的和。可以通过 lower_bound 出边界处的两个元素来计算。中间的数暴力扫一遍,由于查询的区间是连续且不重合的,所以每个数最多会被暴力扫一遍,时间复杂度正确,为 \(O(n\log n)\)

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,ed[200005],x[200005],fr=1,tot;
signed main(){
	cin>>n;
	while(n--){
		int op;cin>>op;
		if(op==1){
			int c;tot++;
			cin>>c>>x[tot];
			ed[tot]=ed[tot-1]+c;
		}
		else{
			int k;cin>>k;
			int l=lower_bound(ed+1,ed+tot+1,fr)-ed;
			int r=lower_bound(ed+1,ed+tot+1,fr+k-1)-ed;
			int ans=(ed[l]-fr+1)*x[l];
			for(int i=l+1;i<=r;i++)ans+=(ed[i]-ed[i-1])*x[i];
			ans-=(ed[r]-(fr+k-1))*x[r];
			cout<<ans<<'\n';
			fr+=k;
		}
	}
	return 0;
} 

D.Make Geometric Sequence

translation:

给定一个数列,判断它是不是等比数列的重排。

首先我们注意到等比数列的绝对值单调,因此先按照绝对值给数列排序。

发现如果公比不为 \(-1\),那么排完序后必须有 \(a_{i-1}a_{i+1}=a_i^2(1<i<n)\),如果没有则不是等比数列,然后特判一下公比为 \(-1\) 的情况即可。

有一些细节,赛时这题吃了 4 发罚时,战犯了。

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,a[200005],n;
bool cmp(int x,int y){
	return abs(x)<abs(y);
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1,cmp);
	if(abs(a[1])==abs(a[n])){
		int cnt=0;
		for(int i=1;i<=n;i++)if(a[i]>0)cnt++;
		if(cnt==n||cnt==0)cout<<"Yes\n";
		else if(n%2==0&&cnt==n/2)cout<<"Yes\n";
		else if(n%2==1&&(cnt==n/2||cnt==n-n/2))cout<<"Yes\n";
		else cout<<"No\n";
		return;
	}
	for(int i=2;i<n;i++){
		if(a[i-1]*a[i+1]!=a[i]*a[i]){
			cout<<"No"<<'\n';
			return;
		}
	}
	cout<<"Yes\n";
}
signed main(){
	cin>>T;
	while(T--)solve();
}

E.Reverse 2^i

translation:

给定一个长度为 \(2^n\) 的排列 \(P\),下标从 \(0\) 起,可以进行以下操作若干次:
选择 \(a,b\) 使得 \(0\le a\times 2^b<(a+1\times 2^b)\le 2^n\),并翻转 \(P_{a\times 2^b}\sim P{(a+1)\times 2^b-1}\) 这一段。
求经过任意次操作后,最小字典序的排列。

随便手玩一下可以发现,对于任意一段区间 \(P_{a\times 2^b}\sim P{(a+1)\times 2^b-1}\),我们总可以把这段区间的最小值放到区间的第一位。具体操作为,如果最小值在前半段则不动,最小值在后半段则翻转,然后递归到左右区间。

显然这是最优解,又因为在这个过程中每个数最多被翻转 \(n\) 次,因此总复杂度 \(O(n2^n)\) 随便过。

code

#include<bits/stdc++.h>
using namespace std;
int T,n,a[262150];
void solve(int l,int r){
	if(l==r)return;
	int ps=l,mid=(l+r)>>1;
	for(int i=l+1;i<=r;i++)if(a[i]<a[ps])ps=i;
	if(ps>mid){
		for(int i=l;i<=mid;i++)swap(a[i],a[r-(i-l)]);
	}
	solve(l,mid);
	solve(mid+1,r);
}
signed main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=(1<<n);i++)cin>>a[i];
		solve(1,1<<n);
		for(int i=1;i<=(1<<n);i++)cout<<a[i]<<" ";
		cout<<'\n';
	}
}

F.No Passage

translation:

给定一个 \(n\times m\) 的网格,其中有一些方格是目标格。现在 A 和 B 在这个网格上玩游戏:
每次 B 从四方向中选择一个方向封禁,A 从剩下三个方向中选择一个方向移动。A 的目标是到达一个目标格,如果可以则最小化移动步数。B 的目标是阻止 A 到达目标格。如果 A 一定可以到达,则 B 会最大化移动步数。
在此情况下,求使 A 可以到达目标格的所有起始点所需移动步数之和。

发现如果一个格子相邻的四格中至少有两个可以到达目标格,那么这个格子也可以到达目标格,所需步数为周边可到达目标格的格子中步数次大值 \(+1\)。所有目标格都可以到达目标格,且步数为 0。然后每次像 BFS 一样更新相邻格,如果当前格更新后邻格可能可以更新,则将邻格加入队列。全部更新一遍之后直接求和即可。

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define F first
#define S second
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,k;
int a[3005][3005];
int du[3005][3005];
queue<pair<int,int> >q;
signed main(){
	cin>>n>>m>>k;
	memset(a,-1,sizeof(a));
	for(int i=1;i<=k;i++){
		int x,y;cin>>x>>y;
		a[x][y]=0;
		for(int j=0;j<4;j++){
			int nx=x+dx[j],ny=y+dy[j];
			if(nx<1||ny<1||nx>n||ny>m)continue;
			q.push({nx,ny});
		}
	}
	while(q.size()){
		int x=q.front().F,y=q.front().S;
		
		q.pop();
//		if(a[x][y]!=-1)continue;
		int cnt=0;
		int m1=1e9,m2=1e9;
		for(int j=0;j<4;j++){
			int nx=x+dx[j],ny=y+dy[j];
			if(nx<1||ny<1||nx>n||ny>m)continue;
			if(a[nx][ny]!=-1){
				cnt++;
				if(a[nx][ny]<m1){
					m2=m1;
					m1=a[nx][ny];
				}
				else if(a[nx][ny]<m2)m2=a[nx][ny];
			}
		}
		if(cnt<=1||(a[x][y]!=-1&&m2+1>=a[x][y]))continue;
		a[x][y]=m2+1;
		for(int j=0;j<4;j++){
			int nx=x+dx[j],ny=y+dy[j];
			if(nx<1||ny<1||nx>n||ny>m)continue;
			if(a[nx][ny]!=-1&&a[nx][ny]<=m2+2)continue;
			q.push({nx,ny});
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]!=-1)ans+=a[i][j];
		}
	}
	cout<<ans<<endl;
	return 0;
}

G.Big Banned Grid

translation:

给定一个 \(n\times m\) 的网格,其中有一些方格有障碍,求 \((1,1)\)\((n,m)\) 是否四方向连通。

\(n,m\le 2\times 10^5\),直接跑搜索显然不现实。所以应用类似平面图转对偶图的思想,障碍与障碍八方向连边,然后跑一次连通块并查集。检查上下边界,左右边界,左上边界,右下边界是否有连通。如果有那么 \((1,1)\)\((n,m)\) 不连通,否则连通。

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define F first
#define S second
const int dx[8]={1,-1,0,0,1,1,-1,-1},dy[8]={0,0,1,-1,1,-1,1,-1};
map<pair<int,int>,int>mp;
queue<int>q;
vector<int>G[200010];
int n,m,k,fa[200010];
int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	x=find(x),y=find(y);
	if(x==y)return;
	fa[x]=y;
}
signed main(){
	cin>>n>>m>>k;
	for(int i=1;i<=k+5;i++)fa[i]=i;
	for(int i=1;i<=k;i++){
		int x,y;cin>>x>>y;
		mp[{x,y}]=i;
		for(int j=0;j<8;j++){
			int nx=x+dx[j],ny=y+dy[j];
			if(nx<1||ny<1||nx>n||ny>m)continue;
			if(mp[{nx,ny}])merge(i,mp[{nx,ny}]);
		}
		if(x==1)merge(i,k+1);
		if(y==1)merge(i,k+2);
		if(x==n)merge(i,k+3);
		if(y==m)merge(i,k+4);
	}
	int f1=find(k+1),f2=find(k+2),f3=find(k+3),f4=find(k+4);
	if(f1==f2||f1==f3||f2==f4||f3==f4)cout<<"No\n";
	else cout<<"Yes\n"; 
	return 0;
}
posted @ 2025-07-07 12:24  Xuan_qwq  阅读(27)  评论(0)    收藏  举报