AtCoder Beginner Contest 289 解题报告

\(\text{By DaiRuiChen007}\)

A. flip

逐位枚举反转即可

时间复杂度 \(\Theta(|s|)\)

#include<bits/stdc++.h>
using namespace std;

signed main() {
	string s;
	cin>>s;
	int n=s.length();
	for(int i=0;i<n;++i) s[i]='0'+((s[i]-'0')^1);
	cout<<s<<"\n";
	return 0;
}

B. V

枚举处理出每个分段再反转即可

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

#include<bits/stdc++.h>
using namespace std;
const int MAXN=101;
int nxt[MAXN];
vector <int> ver[MAXN];
signed main() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) nxt[i]=i;
	for(int i=1;i<=m;++i) {
		int u;
		scanf("%d",&u);
		nxt[u]=u+1;
	}
	for(int i=n;i>=1;--i) nxt[i]=nxt[nxt[i]],ver[nxt[i]].push_back(i);
	for(int i=1;i<=n;++i) {
		for(int j:ver[i]) printf("%d ",j);
	}
	puts("");
	return 0;
}

C. Coverage

\(1\sim n\) 的元素状压然后 dp 解决

时间复杂度 \(\Theta(m2^n)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=12;
int dp[MAXN][1<<MAXN],a[MAXN];
inline int bit(int x) { return 1<<x; }
signed main() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) {
		int c;
		scanf("%d",&c);
		while(c--) {
			int x;
			scanf("%d",&x);
			a[i]|=bit(x-1);
		}
	}
	dp[0][0]=1;
	for(int i=0;i<m;++i) {
		for(int j=0;j<bit(n);++j) {
			dp[i+1][j]+=dp[i][j];
			dp[i+1][j|a[i+1]]+=dp[i][j];
		}
	}
	printf("%d\n",dp[m][bit(n)-1]);
	return 0;
}

D. Step Up Robot

dp 转移维护每个位置是否可达即可

时间复杂度 \(\Theta(nX)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+1;
bool mark[MAXN],dp[MAXN];
int a[MAXN];
signed main() {
	int n,m,x;
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i) {
		int u;
		scanf("%d",&u);
		mark[u]=true;
	}
	scanf("%d",&x);
	dp[0]=true;
	for(int i=0;i<x;++i) {
		for(int j=1;j<=n;++j) {
			if(i+a[j]>x||mark[i+a[j]]) continue;
			dp[i+a[j]]|=dp[i];
		}
	}
	puts(dp[x]?"Yes":"No");
	return 0;
}

E. Swap Places

把两个人所在的位置 \((u,v)\) 看成一个状态整体做一遍 BFS 即可

时间复杂度 \(\Theta(n^2+mn)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2001,INF=1e9;
int col[MAXN],dis[MAXN][MAXN];
vector <int> G[MAXN];
inline void solve() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) {
		G[i].clear();
		for(int j=1;j<=n;++j) dis[i][j]=INF;
	}
	for(int i=1;i<=n;++i) scanf("%d",&col[i]);
	for(int i=1;i<=m;++i) {
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	queue <pair<int,int> > q;
	q.push(make_pair(1,n));
	dis[1][n]=0;
	while(!q.empty()) {
		int u=q.front().first,v=q.front().second;
		q.pop();
		for(int x:G[u]) {
			for(int y:G[v]) {
				if(col[x]^col[y]&&(dis[x][y]>dis[u][v]+1)) {
					dis[x][y]=dis[u][v]+1;
					q.push(make_pair(x,y));
				}
			}
		}
	}
	if(dis[n][1]==INF) dis[n][1]=-1;
	printf("%d\n",dis[n][1]);
}
signed main() {
	int T;
	scanf("%d",&T);
	while(T--) solve();
	return 0;
}

F. Teleporter Takahashi

一个显然的观察是 \((x,y)\) 关于 \((a,b)\) 对称后得到 \((x',y')=(2a-x,2b-y)\) 的操作不改变 \(x,y\) 的奇偶性,因此 \(s_x\equiv t_x\pmod2,s_y\equiv t_y\pmod 2\) 必然成立

注意到关于两个 \(x\) 相同的点对称不改变操作点的 \(x\) 值,因此可以对 \(x,y\) 分开考虑,由于 \(s_x-t_x,s_y-t_y\) 都是 \(2\) 的倍数,因此我们只需要构造单次移动步长为 \(2\) 的移动方法

注意到分别关于 \((a,c),(a+1,c)\) 对称即可向右移动 \(2\),同理向上下左方向移动 \(2\) 步的操作也是显然的

注意特判 \(a=b\)\(c=d\) 的情况

操作次数大约是 \(\frac 12(|s_x-t_x|+|s_y-t_y|)\) 的,可以通过本题

时间复杂度 \(\Theta(X+Y)\),其中 \(X,Y\) 为横纵坐标值域

#include<bits/stdc++.h>
#define int long long
using namespace std;
int sx,sy,tx,ty;
int a,b,c,d;
vector <int> X,Y;
int len=0;
inline void oper(int x,int y) {
	X.push_back(x),Y.push_back(y);
	++len;
	sx=2*x-sx,sy=2*y-sy;
}
inline void Print() {
	puts("Yes");
	for(int i=0;i<len;++i) {
		printf("%lld %lld\n",X[i],Y[i]);
	}
	exit(0);
}
inline void Quit() {
	puts("No");
	exit(0);
}
signed main() {
	scanf("%lld%lld%lld%lld",&sx,&sy,&tx,&ty);
	scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
	if(a==b&&c==d) {
		if(sx==tx&&sy==ty) Print();
		if(sx+tx==2*a&&sy+ty==2*c) {
			oper(a,c);
			Print();
		}
		Quit();
	}
	if(sx%2!=tx%2) {
		puts("No");
		return 0;
	}
	if(sy%2!=ty%2) {
		puts("No");
		return 0;
	}
	if(a==b&&sx!=tx&&sx+tx!=2*a) Quit();
	if(c==d&&sy!=ty&&sy+ty!=2*c) Quit();
	if(a==b&&sx!=tx) oper(a,c);
	if(c==d&&sy!=ty) oper(a,c);
	int gx=abs(sx-tx)/2,gy=abs(sy-ty)/2;
	if(sx<tx) for(int i=1;i<=gx;++i) oper(a,c),oper(a+1,c);
	else for(int i=1;i<=gx;++i) oper(a+1,c),oper(a,c);
	if(sy<ty) for(int i=1;i<=gy;++i) oper(a,c),oper(a,c+1);
	else for(int i=1;i<=gy;++i) oper(a,c+1),oper(a,c);
	Print();
	return 0;
}

G. Shopping in AtCoder store

先将所有顾客按 \(b\) 降序排序,显然能满足的顾客一定是一段连续的前缀,假设满足的顾客是 \(1\sim i\),那么 \(p_j\le b_i+c_j\),根据贪心显然令 \(p_j=b_i+c_j\),因此我们的问题转化为对于所有 \(j\)\(\max_{i=1}^n\{i\times (b_i+c_j)\}\)

注意到 \(i\times (b_i+c_j)=i\times c_j+i\times b_i\),可以看成关于 \(c_j\) 的一次函数 \(y=i\times x+i\times b_i\),那么我们只需要对所有 \(c_j\)\(x=c_j\) 时的最大 \(y\) 值,直接用李超线段树维护即可

由于 \(c_j\) 值域较大,可以先对 \(c_1\sim c_n\) 排序,然后把函数改写为 \(f(x)=i\times c_x+i\times b_i\),由于 \(c_1\sim c_n\) 有序,因此李超线段树需要的单调性依然满足,可以通过本题

时间复杂度 \(\Theta(m\log m+n\log n+m\log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=2e5+1,MAXV=2e5+1;
int n,m,b[MAXN],c[MAXN];
struct line {
    int k,b;
    inline int f(int v) {
        return k*c[v]+b;
    }
    line(int _k=0,int _b=0) { k=_k,b=_b; }
} a[MAXN];
class LCtree {
    private:
        int tree[MAXV<<2];
        inline int left(int x) { return x<<1; }
        inline int right(int x) { return x<<1|1; }
    public:
        inline void Modify(int id,int l=1,int r=m,int pos=1) {
            int mid=(l+r)>>1;
            if(a[id].f(mid)>a[tree[pos]].f(mid)) swap(tree[pos],id);
            if(l==r) return ;
            if(a[id].f(l)>a[tree[pos]].f(l)) Modify(id,l,mid,left(pos));
            if(a[id].f(r)>a[tree[pos]].f(r)) Modify(id,mid+1,r,right(pos));
            return ;
        }
        inline int Query(int x,int l=1,int r=m,int pos=1) {
            int t=a[tree[pos]].f(x);
            if(l==r) return t;
            int mid=(l+r)>>1;
            if(x<=mid) return max(t,Query(x,l,mid,left(pos)));
            if(mid<x) return max(t,Query(x,mid+1,r,right(pos)));
        }
}   S;
int id[MAXN],ans[MAXN];
signed main() {
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;++i) scanf("%lld",&b[i]);
	for(int i=1;i<=m;++i) scanf("%lld",&c[i]);
	for(int i=1;i<=m;++i) id[i]=i;
	sort(id+1,id+m+1,[&](int x,int y){ return c[x]<c[y]; });
	sort(c+1,c+m+1);
	sort(b+1,b+n+1,[&](int x,int y){ return x>y; });
	for(int i=1;i<=n;++i) {
		a[i]=line(i,i*b[i]);
		S.Modify(i);
	}
	for(int i=1;i<=m;++i) {
		ans[id[i]]=S.Query(i);
	}
	for(int i=1;i<=m;++i) printf("%lld ",ans[i]);
	puts("");
	return 0;
}