2020-2021 ACM-ICPC, Asia Seoul Regional Contest 补题|题解

2021.2.5校队训练纪念戳
咕咕咕 慢速补题中qwqwq

cf gym传送门


A-Autonomous Vehicle

#include <bits/stdc++.h>
using namespace std;
#define maxn 3000
int n, T, stx, sty, stp, stdir, stopt, stL, tot, num1[maxn], num2[maxn];
int cnt1, cnt2;
bool mark1[maxn][2][5], mark2[maxn][2][5];

struct node {
	int x, y, tp, tl;
	node(int _x = 0, int _y = 0, int _tp = 0, int _tl = 0) {
		x = _x, y = _y, tp = _tp, tl = _tl;
	}
}P1[maxn][maxn], P2[maxn][maxn], rec[maxn];

struct line {
	int p, a, b, id;
	line(int _p = 0, int _a = 0, int _b = 0, int _id = 0) {
		p = _p, a = _a, b = _b, id = _id;
	}
	friend bool operator <(line a, line b) {
		return a.p < b.p;
	}
}vL[maxn], hL[maxn];

void dfs(int L, int w, int dir, int p, int t) {
//	cout <<"##" << t << " " << T << " " << tot << endl;
//	if(t > T + 1) return;
	//cout << "????" << " " << L << " " << w << " " << dir << " " << p << " " << t << endl;
	if(w == 1) {
		rec[++ tot] = node(P1[L][p].x, P1[L][p].y, t, dir);
		if(p == num1[L]) {
			if(mark1[L][1][dir]) return;
			mark1[L][1][dir] = 1;
		}
		else if(p == 1){
			if(mark1[L][0][dir]) return;
			mark1[L][0][dir] = 1;
		}
		if(dir == 1) {
			if(p == num1[L]) dfs(L, w, 2, p, t);
			else {
				int p1 = P1[L][p + 1].tp, l1 = P1[L][p + 1].tl;
				if(p + 1 != num1[L]) dfs(l1, 2, 3, p1, P1[L][p + 1].y - P1[L][p].y + t);
				else dfs(L, w, 1, p + 1, P1[L][p + 1].y - P1[L][p].y + t);
			}
		}
		else {
			if(p == 1) dfs(L, w, 1, p, t);
			else {
				int p1 = P1[L][p - 1].tp, l1 = P1[L][p - 1].tl;
				if(p - 1 != 1) dfs(l1, 2, 4, p1, P1[L][p].y - P1[L][p - 1].y + t);
				else dfs(L, w, 2, p - 1, P1[L][p].y - P1[L][p - 1].y + t);
			}
		}
	}
	else if(w == 2) {
		rec[++ tot] = node(P2[L][p].x, P2[L][p].y, t, dir);
		if(p == 1) {
			if(mark2[L][0][dir]) return;
			mark2[L][0][dir] = 1;
		}
		else if(p == num2[L]) {
			if(mark2[L][1][dir]) return;
			mark2[L][1][dir] = 1;
		}
		
		if(dir == 3) {
			if(p == 1) dfs(L, w, 4, p, t);
			else {
				int p1 = P2[L][p - 1].tp, l1 = P2[L][p - 1].tl;
			//	cout << "??why: " << L << " " << P2[L][p].x << " " << P2[L][p - 1].x << endl;
				if(p != 2) dfs(l1, 1, 2, p1, t + P2[L][p].x - P2[L][p - 1].x);
				else dfs(L, w, 3, p - 1, t + P2[L][p].x - P2[L][p - 1].x);
				
			}
		}
		else {
			if(p == num2[L]) dfs(L, w, 3, p, t); 
			else {
				int p1 = P2[L][p + 1].tp, l1 = P2[L][p + 1].tl;
				if(p + 1 != num2[L]) dfs(l1, 1, 1, p1, t + P2[L][p + 1].x - P2[L][p].x);
				else dfs(L, w, 4, p + 1, t + P2[L][p + 1].x - P2[L][p].x);
			}
		}
	}
}

int main() {
	cin >> n >> T;
	for(int i = 1; i <= n; i ++) {
		int bx, by, ex, ey;
		cin >> bx >> by >> ex >> ey;
		if(i == 1) {
			stx = bx, sty = by;
			if(bx == ex) {
				if(by > ey) stdir = 2;
				else stdir = 1;
			}
			else {
				if(bx > ex) stdir = 3;
				else stdir = 4;
			}
		}
		if(bx == ex) { // vertical 
			if(by > ey) swap(by, ey); 
			vL[++ cnt1] = line(bx, by, ey, i);
		}
		else { // horizontal
			if(bx > ex) swap(bx, ex);
			hL[++ cnt2] = line(by, bx, ex, i);
		}
	}
	sort(vL + 1, vL + cnt1 + 1);
	sort(hL + 1, hL + cnt2 + 1);
	for(int i = 1; i <= cnt1; i ++) P1[i][++ num1[i]] = node(vL[i].p, vL[i].a, -1, -1);
	for(int i = 1; i <= cnt2; i ++) P2[i][++ num2[i]] = node(hL[i].a, hL[i].p, -1, -1);
	
	for(int i = 1; i <= cnt1; i ++) {
		int p1 = vL[i].p;
		for(int j = 1; j <= cnt2; j ++) {
			int p2 = hL[j].p;
			if(p1 <= hL[j].a || p1 >= hL[j].b) continue;
			if(p2 <= vL[i].a || p2 >= vL[i].b) continue;
			P1[i][++ num1[i]] = node(p1, p2, num2[j] + 1, j);
			P2[j][++ num2[j]] = node(p1, p2, num1[i], i);
		//	cout << "=======" << i << " " << j << " " << num1[i] << " " << num2[j] << " " << p1 << " " << p2 << " " << P1[1][3].tp << endl; 
		}
	//	cout << "======" << i << " " << num1[i] << " " << cnt1 << " " << cnt2 << endl;
	}	
//	cout << "???what : " << " " << P1[1][3].tp << endl;
	for(int i = 1; i <= cnt1; i ++) P1[i][++ num1[i]] = node(vL[i].p, vL[i].b, -1, -1);
	for(int i = 1; i <= cnt2; i ++) P2[i][++ num2[i]] = node(hL[i].b, hL[i].p, -1, -1);
	
	for(int i = 1; i <= cnt1; i ++) 
		if(vL[i].id == 1) {
			if(sty == vL[i].a) stp = 1;
			else stp = num1[i];
			stL = i; stopt = 1;
			break;
		}
	
	for(int i = 1; i <= cnt2; i ++) 
		if(hL[i].id == 1) {
			if(stx == hL[i].a) stp = 1;
			else stp = num2[i];
			stL = i; stopt = 2;
			break;
		}
//	cout << "????" << stx << " " << sty << " " << stp<< endl;
	dfs(stL, stopt, stdir, stp, 0);
	T %= rec[tot].tp;
	//cout << rec[tot].tp << endl;
	for(int i = 1; i < tot; i ++) {
	//	cout << "++++++" << i << " " << rec[i].x << " " << rec[i].y << " " << rec[i].tl << " " << rec[i].tp << endl;
		if(rec[i].tp <= T && rec[i + 1].tp >= T) {
			int dir = rec[i].tl;
			int ansx = rec[i].x, ansy = rec[i].y;
			if(dir == 1) 
				ansy += T - rec[i].tp;
			else if(dir == 2) 
				ansy -= T - rec[i].tp;
			else if(dir == 3) 
				ansx -= T - rec[i].tp;
			else if(dir == 4) 
				ansx += T - rec[i].tp;
			cout << ansx << " " << ansy << endl;
			break;
		}
	}
	return 0; 
}

B-Commemorative Dice

签到题啦~

#include<bits/stdc++.h>
#define MAXN 7
using namespace std;
int a[MAXN], b[MAXN];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("ce.in", "r", stdin);
    #endif
    for(int i = 1; i <= 6; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= 6; i++) scanf("%d", &b[i]);
    int ans1 = 0, ans2 = 36;
    for(int i = 1; i <= 6; i++){
        for(int j = 1; j <= 6; j++){
            if(b[j] < a[i]) ans1++;
        }
    }
    for(int i = 2; i <= ans1; i++){
        while(ans2 % i == 0 && ans1 % i == 0) ans2 /= i, ans1 /= i;
    }
    printf("%d/%d\n", ans1, ans2);
    return 0;
}

C-Dessert Café

给定一棵树,树上有一些特殊点。求这些特殊点之间的路径上点的个数(包括特殊点自己)
那么我们只需要记录一下key_cnt[i]表示i这个子树里面有几个关键节点。可发现,如果节点i有任意一个儿子v,如果key_cnt[v]==k,那么节点i就不在路径上,反之一定在。

#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;
int n, k, t, ans;
int head[MAXN], key[MAXN], key_cnt[MAXN];
struct Edge{int nxt, to, dis;}edge[MAXN << 1];
inline void add(int from, int to, int dis){
	edge[++t].nxt = head[from], edge[t].to = to, edge[t].dis = dis;
	head[from] = t;
}
inline void pre_solve(int x, int f){
	if(key[x] == 1) key_cnt[x]++;
	for(int i = head[x]; i; i = edge[i].nxt){
		int v = edge[i].to;
		if(v == f) continue;
		pre_solve(v, x);
		key_cnt[x] += key_cnt[v];
	}
	return;
}
inline void solve(int x, int f, bool flag){
	if(!key_cnt[x]) return;
	ans++;
	// printf("++ now=%d\n", x);
	for(int i = head[x]; i; i = edge[i].nxt){
		int v = edge[i].to;
		if(v == f) continue;
		if(key_cnt[v]){
			if(flag == true || key[x] == 1) solve(v, x, true);
			else solve(v, x, false);
            if(key_cnt[v] == k){
                // printf("-- now=%d\n", x);
                ans--;
            }
		}
	}
	return;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%d%d", &n, &k);
	int a, b, c;
	for(int i = 1; i < n; i++){
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	}
	for(int i = 1; i <= k; i++){
		scanf("%d", &a);
		key[a] = 1;
	}
	pre_solve(1, 0);
	// for(int i = 1; i <= n; i++) printf("key_cnt[%d]=%d\n", i, key_cnt[i]);
	solve(1, 0, false);
	printf("%d\n", ans);
	return 0;
}

D-Electric Vehicle

E-Imprecise Computer

一共有0,1,2三种情况,我们需要调整上下两个round的比较关系来满足有合法序列。而我们可以发现,如果我们更改一个关系,会改变这个关系相连的两个数的值(一个+1,一个-1)。而又因为相差1同时包括(+1,-1)两种情况,所以相当于我们一次可以把两个相邻的(1,1)变成(0,0)。同理,我们可以把(1,0)变成(0,1)(或者是(0,1)改成(1,0)qwq)
综上,我们从前往后扫一遍

  • 如果是0的话,不需要操作
  • 如果是1的话,可以和前面的1一起消为0
  • 如果是2的话,改成向前传递一个1,向后传递一个1,自己变成0
    由此可以得出简化之后的做法:
    我们现在有两个变量a,b,初始值为0。从前往后扫一遍,如果遇到1,选取a或者b XOR 1(如果能消为0,优先)。如果遇到2,由于2必须往前面传递一个1,所以要求此时a XOR b 必须为1.
    代码如下
#include<bits/stdc++.h>
#define MAXN 1000010
using namespace std;           
int n, a, b;
int m[MAXN];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("ce.in", "r", stdin);
    #endif
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &m[i]);
    for(int i = 1; i <= n; i++){
        if(m[i] == 1){
            if(a == 1) a ^= 1;
            else if(b == 1) b ^= 1;
            else a ^= 1;
        }
        else if(m[i] == 2){
            if(a ^ b != 1){
                printf("NO\n");
                return 0;
            }
        }
    }
    if(a == 0 && b == 0) printf("YES\n");
    else printf("NO\n");
    return 0;
}

F-Ink Mix

G-Mobile Robot

由于机器人不能最后在相同位置上,所以最终一定是一个等差数列。分类讨论递增数列和递减数列即可。
考虑固定第一个机器人的位置,那么之后的机器人的位置就都知道了。我们现在要求
m i n ( m a x ( a i − ( a 1 + d ∗ ( i − 1 ) ) ) min(max(a_i - (a_1 + d * ( i -1))) min(max(ai(a1+d(i1)))
m i n ( m a x ( a i − ( a 1 − d ∗ ( i − 1 ) ) ) min(max(a_i - (a_1 - d * ( i -1))) min(max(ai(a1d(i1)))
上述两个式子的最小值。
对于每个式子,我们只需要求出来扫一遍的最大位置偏差和最小位置偏差即可。
调整 a 1 a_1 a1的位置,就是 m a x x − m i n n 2 \frac{maxx - minn}{2} 2maxxminn

#include<bits/stdc++.h>
#define MAXN 1000010
using namespace std;
int n;
long long d;
long long a[MAXN];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("ce.in", "r", stdin);
    #endif
    scanf("%d%lld", &n, &d); 
    for(int i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
    }
    long long cur = a[1], minn1 = 0, maxx1 = 0; 
    for(int i = 2; i <= n; i++){
        cur += d;
        long long cha = a[i] - cur;
        minn1 = min(minn1, cha);
        maxx1 = max(maxx1, cha);
    }
    
    cur = a[1];
    long long minn2 = 0, maxx2 = 0;
    for(int i = 2; i <= n; i++){
        cur -= d;
        long long cha = a[i] - cur;
        minn2 = min(minn2, cha);
        maxx2 = max(maxx2, cha);
    }
    if(maxx1 - minn1 < maxx2 - minn2){
        printf("%lld.%d\n", (maxx1 - minn1) / 2, (maxx1 - minn1) % 2 == 0 ? 0 : 5);
    }
    else{
        printf("%lld.%d\n", (maxx2 - minn2) / 2, (maxx2 - minn2) % 2 == 0 ? 0 : 5);
    }
    return 0;
} 

H-Needle

什么FFT裸题?我怎么不会啊
考虑bitset(时间复杂度大概是 m 2 / 64 m^2/64 m2/64
我们考虑有ABC三个bitset(1表示该位置有洞,0表示没有),然后枚举向右移动向左移动的距离i,然后(A>>i) & B & (C<<i) 里面的1的个数就是可以穿过的线的数量啦!(上面说的这个是从左上到右下的斜线,同理颠倒移动的方向,可以求出来从右上到左下的斜线的个数,加起来就是答案了)
PS.注意 不要将三点竖直下来的线算两次qwq

#include<bits/stdc++.h>
#define MAXN 30000
using namespace std;
int base = 30000;
int n[3];
bitset<MAXN * 2 + 5>a[3];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("ce.in", "r", stdin);
    #endif
    int cur;
    for(int i = 0; i < 3; i++){
        scanf("%d", &n[i]);
        for(int j = 0; j < n[i]; j++){
            scanf("%d", &cur);
            a[i].set(cur + base);
        }
    }
    int ans = 0;
    bitset<MAXN * 2 + 5> new1 = a[0];
    bitset<MAXN * 2 + 5> new2 = a[2];
    bitset<MAXN * 2 + 5> curcur;
    for(int i = 0; i < base; i++){
        curcur = new1 & a[1];
        curcur = curcur & new2;
        int cnt2 = curcur.count();
        ans += cnt2;
        new1 >>= 1;
        new2 <<= 1;
    }
    new1 = a[0] << 1, new2 = a[2] >> 1;
    for(int i = 1; i < base; i++){
        curcur = new1 & a[1];
        curcur = curcur & new2;
        int cnt2 = curcur.count();
        ans += cnt2;
        new1 <<= 1;
        new2 >>= 1;
    }
    printf("%d\n", ans);
    return 0;
}

I-Stock Analysis

J-Switches

K-Tiling Polyomino

L-Two Buildings

决策单调性优化DP
首先需要有一个结论(其实也显而易见),假如对于一个点i来说,它的最佳点在j(i < j),那么对于任意一个点p(p < i),p的最佳点一定<=j。
那么依据此结论,我们可以确定有决策单调性。
首先我们从最左找递增序列(因为如果存在i<j且h[i]>h[j],显而易见j去匹配最佳点不会更优),同理,从最右开始找反向递增序列。我们将其分别存在数组a,b中。之后便可以从数组a中匹配数组b的点,来更新ans。
设solve(l,r,ll,rr)表示解决区间(l,r),他们最佳点在区间(ll,rr)中的匹配子问题,我们如果算出来了mid的最佳点,就可以分治下去计算。分治一共不超过logn层,每一层上暴力寻找,相当于最多每个点不会超过nlogn次访问,所以时间复杂度有保障。

#include<bits/stdc++.h>
#define MAXN 1000010
using namespace std;
int n, cnta, cntb;
int h[MAXN], a[MAXN], b[MAXN], pos[MAXN];
long long ans;
inline long long calc(int i, int j){
	return 1ll * (h[a[i]] + h[b[j]]) * (b[j] - a[i]);
}
inline void solve(int l, int r, int ll, int rr){
	if(l > r || ll > rr) return;
	int mid = (l + r) / 2;
	for(int i = rr; i >= ll; i--){
		if(!pos[mid]) pos[mid] = i;
		else{
			long long cur1 = calc(mid, pos[mid]);
			long long cur2 = calc(mid, i);
			if(cur1 < cur2){
				pos[mid] = i;
				ans = max(ans, cur2);
			}
		}
	}
	ans = max(ans, calc(mid, pos[mid]));
	if(l != r) solve(l, mid, ll, pos[mid]);
	solve(mid + 1, r, pos[mid], rr);
	return;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &h[i]);
	for(int i = 1; i <= n; i++){
		if(cnta == 0) a[++cnta] = i;
		else if(h[i] > h[a[cnta]]) a[++cnta] = i;
	}
	for(int i = n; i >= a[cnta]; i--){
		if(cntb == 0) b[++cntb] = i;
		else if(h[i] > h[b[cntb]]) b[++cntb] = i;
	}
	reverse(&b[1], &b[cntb + 1]);
	solve(1, cnta, 1, cntb);
	printf("%lld\n", ans);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值