【题解】ABC289 E-G

现在 ABC 的 G 竟然沦落到我都能做出来的地步了。

E.Swap Places

题目分析:

这个题初看可能不很好好做的样子,但是看到它的数据范围是个 \(O(n^2)\) 随便跑的复杂度,所以直接让两个人从 \(1\)\(n\) 去走就好了。
就是一个类似 \(bfs\) 的过程,为了让复杂度对一些,要保证走过的点对不再走了。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3+5;
struct node{
	int len,x,y;
};
vector<int> g[N];
int dp[N][N],c[N];
int main(){
	int t;scanf("%d",&t);
	while(t--){
		int n,m;scanf("%d%d",&n,&m);
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				dp[i][j] = -1;
			}
		}
		for(int i=1; i<=n; i++)	scanf("%d",&c[i]);
		for(int i=1; i<=m; i++){
			int from,to;scanf("%d%d",&from,&to);
			g[from].push_back(to);
			g[to].push_back(from);
		}
		queue<node> q;
		q.push({0,1,n});
		while(!q.empty()){
			node now = q.front();q.pop();
			if(dp[now.x][now.y]!=-1)	continue;
			dp[now.x][now.y] = now.len;
			for(int i : g[now.x]){
				for(int j : g[now.y]){
					if(c[i] != c[j]){
						q.push({now.len+1,i,j});
					}
				}
			}
		}
		printf("%d\n",dp[n][1]);
		for(int i=1; i<=n; i++)	g[i].clear();
	}
	return 0;
}

F.Teleporter Takahashi

题目分析:

对于一个点 \((a,b)\) 关于 \((x,y)\) 的对称点为:\((2\times x - a,2\times y - b)\),所以我们可以考虑对于横纵坐标分开考虑。
首先可以发现,我们的对称不会改变原来的横纵坐标的奇偶性,所以如果给定的和目标点的横纵坐标奇偶性不同,则无解。
顺着这个思路手模一下就会发现,如果我们将 \((a,b)\) 先对于 \((c,d)\) 对称再对于 \((c+1,d)\) 对称,那么我们得到的点的坐标就是:\((a+2,b)\)
也就是说我们这样的操作可以使得横坐标加 \(2\) 而不影响纵坐标,所以同理可以得到减 \(2\) 的推法。
那么就使用这个加 \(2\) 和减 \(2\) 的操作去一点点弥补横纵坐标的差距就好了。
需要特判一下矩阵的横纵坐标只有一种的情况。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > v;
int sx,sy,tx,ty,a,b,c,d;
void move(int x,int y){
	v.push_back({x,y});
	sx = 2 * x - sx;
	sy = 2 * y - sy;
}
int main(){
	scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
	scanf("%d%d%d%d",&a,&b,&c,&d);
	if((sx - tx) % 2 != 0 || (sy - ty) % 2 != 0){
		printf("No\n");
		return 0;
	}
	if(a == b && sx != tx)	move(a,c);  //只能转一次,所以不行就不行了 
	if(c == d && sy != ty)	move(a,c);
	if(a == b && sx != tx){
		printf("No\n");
		return 0;
	}
	else if(sx != tx){
		while(sx > tx)	move(a+1,c),move(a,c);
		while(sx < tx)	move(a,c),move(a+1,c); 
	}
	
	if(c == d && sy != ty){
		printf("No\n");
		return 0;
	}
	else if(sy != ty){
		while(sy > ty)	move(a,c+1),move(a,c);
		while(sy < ty)	move(a,c),move(a,c+1);
	}
	
	printf("Yes\n");
	for(auto i : v)	printf("%d %d\n",i.first,i.second);
	return 0;
}

G.Shopping in AtCoder store

题目分析:

我们可以发现如果选定了某一个价格,那么能购买他的人一定是 \(b\) 数组从大到小排序后的一个前缀。
稍微手推一下式子,算一下贡献的话就可以得到,如果要对于每一个 \(C_i\) 单独求解,必须做到能快速维护区间加\(+\)求区间最大前缀和,看上去就不大好做。
那么就看看是不是有什么单调性啊:显然是有的。
当我们的 \(C_i\) 增大的时候,最优的价格一定是不升的,可以考虑计算 \(C_i\) 变化的贡献就可以显然证明了。
这也就是决策单调性了,用分治去求就可以 \(O(n\log n)\) 解决问题了。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
struct node{
	int pos,val;
}c[N];
int b[N],ans[N];
bool cmp(node a,node b){
	return a.val < b.val;
}
bool cmp2(int a,int b){
	return a > b;
}
void solve(int l1,int r1,int l2,int r2){
	if(l1 > r1 || l2 > r2)	return;
	int val = 0,pos = 0,mid = (l2 + r2)>>1;
	for(int i=l1; i<=r1; i++){
		int res = i * (b[i] + c[mid].val);
		if(res > val)	val = res,pos = i;
	}
	ans[c[mid].pos] = val;
	solve(l1,pos,l2,mid-1);solve(pos,r1,mid+1,r2);
}
signed main(){
	int n,m;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].val),c[i].pos = i;
	sort(b+1,b+n+1,cmp2);sort(c+1,c+m+1,cmp);
	solve(1,n,1,m);
	for(int i=1; i<=m; i++)	printf("%lld ",ans[i]);
	return 0;
}
posted @ 2023-02-13 16:18  linyihdfj  阅读(39)  评论(0编辑  收藏  举报