[题解]AtCoder Beginner Contest 408(ABC408) A~G

A - Timeout

根据题意,若令\(T[0]=0\),则:

  • 答案为Yes\(\iff\)对于\(i\in [1,n]\),都有\(T[i]-T[i-1]\le S\)

时间复杂度\(O(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,s,last;
signed main(){
	cin>>n>>s;
	for(int i=1,x;i<=n;i++){
		cin>>x;
		if(x-last>s) cout<<"No\n",exit(0);
		last=x;
	}
	cout<<"Yes\n";
	return 0;
}

B - Compression

先排序,再使用unique函数进行去重即可。时间复杂度\(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define N 110
using namespace std;
int n,a[N];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	int len=unique(a,a+1+n)-a-1;
	cout<<len<<"\n";
	for(int i=1;i<=len;i++) cout<<a[i]<<" ";
	return 0;
}

也可以使用set等数据结构来实现。时间复杂度是\(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
set<int> s;
signed main(){
	cin>>n;
	for(int i=1,x;i<=n;i++) cin>>x,s.insert(x);
	cout<<s.size()<<"\n";
	for(int i:s) cout<<i<<" ";
	return 0;
}

C - Not All Covered

定义桶数组\(b\)。对于\(i\in [1,m]\),将\(b[L_i\sim R_i]\)每个位置\(+1\),需要前缀和优化。

最后答案即为\(\min\limits_{i=1}^n b[i]\)

时间复杂度\(O(n+m)\)

点击查看代码
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int n,m,a[N],ans=INT_MAX;
signed main(){
	cin>>n>>m;
	for(int i=1,l,r;i<=m;i++) cin>>l>>r,a[l]++,a[r+1]--;
	for(int i=1;i<=n;i++){
		a[i]+=a[i-1];
		ans=min(ans,a[i]);
	}
	cout<<ans<<"\n";
	return 0;
}

D - Flip to Gather

赛时思路

定义\(a[i]\)\(s[1\sim i]\)\(1\)的个数。

如果最终将\([l,r]\)区间置为\(1\)(由于区间可以为空,所以规定\(r\ge l-1\)),那么操作次数就是:

$[1,l)$中$1$的个数 $+$ $[l,r]$中$0$的个数 $+$ $(r,n]$中$1$的个数
即: $$(a[l-1])+(r-l+1-a[r]+a[l-1])+(a[n]-a[r])$$ 整理得: $$a[n]+(2a[l-1]-(l-1))-(2a[r]-r)$$ 令$t[i]=2a[i]-i$,则上式$=$ $$a[n]+t[l-1]-t[r]$$ 由于$r\ge l-1$,所以我们可以枚举$r$,对于每个$r$,找$t[0\sim r]$的前缀最小值作为$t[l-1]$。

最终答案即为:

\[\min\limits_{0\le r\le n} \big[a[n]+\min\limits_{i=0}^r(t[i])-t[r]\big] \]

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 200010
using namespace std;
int t,n,a[N];
string s;
signed main(){
	cin>>t;
	while(t--){
		cin>>n>>s;
		s=' '+s;
		for(int i=1;i<=n;i++) a[i]=a[i-1]+s[i]-'0';
		int tmp=a[n],ans=LLONG_MAX;
		for(int i=1;i<=n;i++) a[i]=2*a[i]-i;
		for(int i=0,minn=LLONG_MAX;i<=n;i++){
			minn=min(minn,a[i]);
			ans=min(ans,minn-a[i]);
		}
		cout<<ans+tmp<<"\n";
	}
	return 0;
}

题解思路

本质上差不多。

定义\(a[i]\)\(s[1\sim i]\)\(0\)的个数,\(b[i]\)\(s[1\sim i]\)\(1\)的个数。

如果最终将\([l,r]\)区间置为\(1\),那操作次数就是:

\[b[l-1]+a[r]-a[l-1]+b[n]-b[r] \]

\(t[i]=b[i]-a[i]\),整理得:

\[b[n]-t[r]+t[l-1] \]

统计起来就和上面同理了。

E - Minimum OR Path

为了让答案尽可能小,我们尽可能要让答案最高位为\(0\),其次要尽可能让答案次高位为\(0\)……于是考虑贪心。

定义集合\(S=\varnothing\)\(S\)中若存在\(i\),则表示“所经过的边,第\(i\)位必须为\(0\)”。

从最高位开始,依次考虑每一位\(k\)

  • 如果在\(S\cup\{k\}\)的限制下,仍然能从\(1\)走到\(n\),则令\(S\leftarrow S\cup\{k\}\)
  • 否则,将答案累加\(2^k\)

第一条的判定可以使用并查集或者DFS来实现。

时间复杂度分别是\(O((n+m)\alpha(n)\log V)\)\(O((n+m)\log V)\)

并查集
#include<bits/stdc++.h>
#define N 200010
#define M 200010
#define int long long
using namespace std;
int n,m,ans,fa[N];
struct edge{int u,v,w;}e[M];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int u,int v){u=find(u),v=find(v);if(u!=v) fa[u]=v;}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
	int s=0;
	for(int i=29;~i;i--){
		s^=(1<<i);
		for(int j=1;j<=n;j++) fa[j]=j;
		for(int j=1;j<=m;j++) if(!(e[j].w&s)) merge(e[j].u,e[j].v);
		if(find(n)!=find(1)) s^=(1<<i),ans^=(1<<i);
	}
	cout<<ans<<"\n";
	return 0;
}
DFS
#include<bits/stdc++.h>
#define N 200010
#define int long long
using namespace std;
int n,m,ans;
struct edge{int to,w;};
vector<edge> G[N];
bitset<N> vis;
void add(int u,int v,int w){G[u].emplace_back(edge{v,w});}
void dfs(int u,int x){
	vis[u]=1;
	for(auto i:G[u]) if(!vis[i.to]&&!(i.w&x)) dfs(i.to,x);
}
signed main(){
	cin>>n>>m;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		add(u,v,w),add(v,u,w);
	}
	int s=0;
	for(int i=29;~i;i--){
		vis=0,dfs(1,s|(1<<i));
		if(vis[n]) s|=(1<<i);
		else ans|=(1<<i);
	}
	cout<<ans<<"\n";
	return 0;
}

F - Athletic

下文的\(a\)表示高度数组。

考虑DP。令\(f[i]\)表示首次移动到\(i\)的最大可能移动次数。

那么从高到低遍历每个脚手架\(i\),有转移:

\[f[i]=1+\max\limits_{|j-i|\le R,a_j\ge a_i+D} f[j] \]

如果没有\(a_j\ge a_i+D\)的限制,直接用线段树来维护区间\(\max\)即可。

现在加上了这条限制,我们仅需在遍历到高度\(i\)时,才将高度为\(i+D\)\(f\)值加入线段树,加入之前初始值都是\(-1\)。这样就满足了线段树里的脚手架高度都\(\ge i+D\)的条件,并且未加入的脚手架不会产生贡献。

时间复杂度\(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define N 500010
#define int long long
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
int n,d,r,a[N],pos[N],f[N];
struct SEG{
	int maxx[N<<2];
	void upd(int x){maxx[x]=max(maxx[lc],maxx[rc]);}
	void sep(int x,int a,int v,int l,int r){
		if(l==r) return maxx[x]=v,void();
		int mid=(l+r)>>1;
		if(a<=mid) sep(lc,a,v,l,mid);
		else sep(rc,a,v,mid+1,r);
		upd(x);
	}
	int query(int x,int a,int b,int l,int r){
		if(a<=l&&r<=b) return maxx[x];
		int mid=(l+r)>>1,ans=-1e9;
		if(a<=mid) ans=max(ans,query(lc,a,b,l,mid));
		if(b>mid) ans=max(ans,query(rc,a,b,mid+1,r));
		return ans;
	}
}tr;
signed main(){
	memset(tr.maxx,-1,sizeof tr.maxx);
	cin>>n>>d>>r;
	for(int i=1;i<=n;i++) cin>>a[i],pos[a[i]]=i;
	for(int i=n;i;i--){//高度
		if(i+d<=n) tr.sep(1,pos[i+d],f[i+d],1,n);
		f[i]=1+tr.query(1,max(1ll,pos[i]-r),min(n,pos[i]+r),1,n);
	}
	cout<<*max_element(f+1,f+1+n)<<"\n";
	return 0;
}

G - A/B < p/q < C/D

原题:P5179。受这篇题解启发很大。

定义\(f(\frac{A}{B},\frac{C}{D})\)\((P,Q)\)使得:

  • \(\frac{A}{B}<\frac{P}{Q}<\frac{C}{D}\)
  • \(Q\)尽可能小。

考虑如何计算\(f(\frac{A}{B},\frac{C}{D})\)

  • 如果\(\frac{A}{B}<1\)\(\frac{C}{D}>1\),答案就是\((1,1)\)

  • 否则将\(\frac{A}{B}\)\(\frac{C}{D}\)同时取倒数得到\(\frac{B}{A}\)\(\frac{D}{C}\),再同时减去\(\lfloor\frac{B}{A}\rfloor\)得到\(\frac{B'}{A}\)\(\frac{D'}{C}\)

    计算\(f(\frac{B'}{A},\frac{D'}{C})\)得到\((p',q')\),按上面的步骤即可反解出原来的\((p,q)\)

正确性证明下午写。

时间复杂度与gcd相同,是\(O(T\log V)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,a,b,c,d,p,q;
void gcd(int a,int b,int &p,int &q,int c,int d){
	if(a<b&&c>d) p=1,q=1;
	else{
		gcd(d%c,c,q,p,b-(d/c)*a,a);
		q+=(d/c)*p;
	}
}
signed main(){
	cin>>t;
	while(t--){
		cin>>a>>b>>c>>d;
		gcd(a,b,p,q,c,d);
		cout<<q<<"\n";
	}
	return 0;
}
posted @ 2025-05-31 23:50  Sinktank  阅读(733)  评论(5)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.