现在 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;
}