AtCoder Beginner Contest 212 题解

比赛地址:https://atcoder.jp/contests/abc212

只有 ABCDEF 的题解,GH 不会。

A

模拟。

void mian(){
	int a,b;scanf("%d%d",&a,&b);
	if(0<a&&b==0)puts("Gold");
	else if(a==0&&0<b)puts("Silver");
	else puts("Alloy");
}

B

模拟。

int a[10];

inline int check(int i,int j){
	if(a[j]==a[i]+1||(a[i]==9&&a[j]==0))return 1;
	return 0;
}

void mian(){
	for(int i=1;i<=4;i++)
		scanf("%1d",a+i);
	if(a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4])puts("Weak");
	else if(check(1,2)&&check(2,3)&&check(3,4))puts("Weak");
	else puts("Strong");
}

C

\(b\) 从小到大排序。然后枚举 \(a_i\),找到 \(b\) 中离 \(a_i\) 最“近”的两个数,更新答案。

typedef long long ll;

const int N=2e5;

int n,a[N+10],m,b[N+10];

inline int find1(int x){
	int l=1,r=m,ans=-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(b[mid]>=x)ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}

inline int find2(int x){
	int l=1,r=m,ans=-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(b[mid]<=x)ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}

void mian(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	for(int i=1;i<=m;i++)
		scanf("%d",b+i);
	std::sort(b+1,b+m+1);
	int ans=0x3f3f3f3f*2;
	for(int i=1;i<=n;i++){
		int x=find1(a[i]),y=find2(a[i]);
		if(x!=-1)ans=std::min(ans,b[x]-a[i]);
		if(y!=-1)ans=std::min(ans,a[i]-b[y]);
	}
	printf("%d\n",ans);
}

D

维护一个小根堆和一个操作 \(2\) 加了多少的 \(tag\)。这个题就变成了这样:

  • 操作 \(1\):将 \(x-tag\) 插进堆中。
  • 操作 \(2\)\(tag\gets tag+x\)
  • 操作 \(3\):输出堆顶元素 \(+\ tag\)
typedef long long ll;

void mian(){
	std::priority_queue<ll,std::vector<ll>,std::greater<ll> > q;
	int m;scanf("%d",&m);
	ll tag=0;
	while(m--){
		int opt,x;
		scanf("%d",&opt);
		if(opt==3)printf("%lld\n",q.top()+tag),q.pop();
		if(opt==1)scanf("%d",&x),q.push(x-tag);
		if(opt==2)scanf("%d",&x),tag+=x;
	}
}

E

下面认为 \(n,m,k\) 同阶、

\(f_{i,j}\) 表示走了 \(i\) 步,第 \(i\) 步走到了 \(j\) 的答案。则:

\[f_{i,j}=\sum_{(k,j)\in G}f_{i-1,k} \]

直接暴力 dp 的复杂度是 \(\mathcal O(n^3)\)

考虑优化:

\[f_{i,j}=\sum_{k=1}^nf_{i-1,k}-\sum_{(k,j)\notin G}f_{i-1,k} \]

前面那个 \(\sum\) 可以事先算出来。

由于边数只有 \(\mathcal O(n)\) 条,所以每枚举一次 \(i\),计算后面那个 \(\sum\) 的复杂度是 \(\mathcal O(n)\)。总复杂度 \(\mathcal O(n^2)\)

typedef long long ll;

const int N=5e3;
const int P=998244353;

int n,m,k,f[N+10][N+10];
int G[N+10][N+10];
std::vector<int> g[N+10];

void mian(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		G[u][v]=G[v][u]=1;
		g[u].push_back(v),g[v].push_back(u);
	}
	for(int i=1;i<=n;i++)G[i][i]=1,g[i].push_back(i);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			G[i][j]=1-G[i][j];
	f[0][1]=1;
	int sum=0;
	for(int i=1;i<=k-1;i++){
		sum=0;
		for(int j=1;j<=n;j++)
			sum+=f[i-1][j],sum%=P;
		for(int j=1;j<=n;j++){
			for(std::vector<int>::iterator it=g[j].begin();it!=g[j].end();it++)
				sum-=f[i-1][*it],sum+=P,sum%=P;
			f[i][j]=sum;
			for(std::vector<int>::iterator it=g[j].begin();it!=g[j].end();it++)
				sum+=f[i-1][*it],sum%=P;
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		if(G[i][1]==1)ans+=f[k-1][i],ans%=P;
	printf("%d\n",ans);
}

F

把每个询问拆成三个步骤:

  1. 这个人坐到的第一辆车是哪辆?
  2. 在时间还在 \(z\) 之前时,他最后坐到的车是哪辆?
  3. 他最后的最后是在车上还是在城市里?

显然 1. 和 3. 二分查找即可,2. 用倍增实现:设 \(f(i,j)\) 表示当前在第 \(i\) 辆车,经过 \(2^j\) 辆车后在哪辆车,则 \(f(i,j)=f(f(i,j-1),j-1)\)

细节特别多,所以我在代码里会有一些注释:

#include<bits/stdc++.h>

void mian();
int main(){
	int T=1;
//	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

typedef long long ll;

const int N=1e5;
const int LOGN=20;

int n,m,q;
int a[N+10],b[N+10],s[N+10],t[N+10];
std::vector<std::pair<int,int>> G[N+10];
int f[N+10][LOGN+5];

void mian(){
	// 读入
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%d",a+i,b+i,s+i,t+i);
		G[a[i]].push_back(std::make_pair(s[i],i));
	}
	// 处理 f 数组
	for(auto &vec:G)std::sort(vec.begin(),vec.end());
	for(int i=1;i<=m;i++){
		auto itr=std::lower_bound(G[b[i]].begin(),G[b[i]].end(),std::make_pair(t[i],0));
		if(itr==G[b[i]].end())f[i][0]=i;
		else f[i][0]=itr->second;
	}
	for(int j=1;j<=LOGN;j++)
		for(int i=1;i<=m;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	while(q--){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		// 第 1. 步
		auto itr=std::lower_bound(G[y].begin(),G[y].end(),std::make_pair(x,0));
		if(itr==G[y].end())printf("%d\n",y); // 他一辆车都坐不到
		else{
			int now=itr->second; // 他现在在哪辆车
			if(z<=s[now])printf("%d\n",y); // 他能坐的第一辆车在 z 时刻之后才开走
			else if(z<=t[now])printf("%d %d\n",a[now],b[now]); // 他正好在坐第一辆车
			else{
				// 第 2. 步
				// 倍增
				for(int i=LOGN;~i;i--)
					if(t[f[now][i]]<z)now=f[now][i];
				// 第 3. 步
				itr=std::lower_bound(G[b[now]].begin(),G[b[now]].end(),std::make_pair(t[now],0));
				if(itr==G[b[now]].end())printf("%d\n",b[now]); // 他走完第 2. 步后就不再坐车了
				else{
					int nxt=itr->second;
					if(z<=s[nxt])printf("%d\n",b[now]); // 他坐不了下一辆车
					else printf("%d %d\n",a[nxt],b[nxt]); // 他在坐下一辆车
				}
			}
		}
	}
}
posted @ 2021-08-02 11:01  registerGen  阅读(158)  评论(0编辑  收藏  举报