2019 ACM-ICPC, Asia Nanjing Regional

2019 ACM-ICPC, Asia Nanjing Regional

题目ABCDEFGHIJK
solved------🚫

✔:比赛时通过;🚫:赛后通过;⚪:比赛时尝试了未通过;-:比赛时未尝试

REPLY

Sstee1XD:

  • 我到底会不会记忆化搜索?

A - A Hard Problem

solved by Sstee1XD. 0:09(+)
题意:给你 1 1 1~ n n n的序列,求最小的k能使每个k大小的区间里都存在两个数,它们有倍数关系。
题解:最小的倍数是 2 2 2,所以分下奇偶就直接开写。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;

#define endl "\n"
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () {	cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' '; err(args...);}

inline int rd () {
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;
int n;
void run () {
	n = rd();
	if (n & 1) {
		printf("%d\n", n - 1 - (n - 1) / 2 + 2);
	}
	else printf("%d\n", n - n / 2 + 1);
}

int main () {
	int _T;
	_T = rd();
	while (_T--) run(); 
    return 0;
}

C - Digital Path

solved by Sstee1XD & lllllan. 03:22(+4)

题意:你需要在矩阵里找路径的数量。
路径应符合以下规定:

  • 每次只能上下左右扩展
  • 每次扩展到的值应比上一个值大 1 1 1
  • 长度至少为 4 4 4
  • 不能再继续扩展

题解:对于一个点来说,它能向大的地方扩展的数量是确定的,所以我们采用记忆化搜索来写。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;

#define endl "\n"
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () {	cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' '; err(args...);}

inline int rd () {
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3 + 7;
const ll mod = 1e9 + 7;
int n, m;
int maps[maxn][maxn];
ll dp[maxn * maxn][5];
int vis[maxn][maxn];
int go[][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int sx[1000007], sy[1000007];
int f;
int checkF(int x, int y) {
	int flag = 0;
	for (int i = 0; i < 4; ++i) {
		int fx = x + go[i][0], fy = y + go[i][1];
		if (fx < 1 || fx > n || fy < 1 || fy > m) continue;
		if (maps[fx][fy] == maps[x][y] - 1) return 0;
		if (maps[fx][fy] - maps[x][y] == 1) flag = 1;
	}
	return flag;
}

int check(int x, int y, int v) {
	if (x < 1 || x > n || y < 1 || y > m) return 0;
	if (maps[x][y] - v != 1) return 0;
	return 1;
}

int get(int x, int y) {
	return (x - 1) * m + y;
}

void dfs(int x, int y, int s) {
	int flag = 0;
	vis[x][y] = 1;
	for (int i = 0; i < 4; ++i) {
		int fx = x + go[i][0], fy = y + go[i][1];
		if (check(fx, fy, maps[x][y])) {
			flag = 1;
			if (!vis[fx][fy]) {
				dfs(fx, fy, s + 1);
			}
			for (int j = 1; j <= 4; ++j) {
				dp[get(x, y)][min(1 + j, 4)] += dp[get(fx, fy)][j];
				dp[get(x, y)][min(1 + j, 4)] %= mod;
			}
		}
	}
	if (!flag) dp[get(x, y)][1]++, dp[get(x, y)][1] %= mod;
}
void run () {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			scanf("%d", &maps[i][j]);
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (checkF(i, j)) sx[f] = i, sy[f++] = j;
		}
	}
	ll ans = 0;
	for (int i = 0; i < f; ++i) {
		dfs(sx[i], sy[i], 1);
		ans += dp[get(sx[i], sy[i])][4];
		ans %= mod;
	}
	printf("%lld\n", ans);
}

int main () {
	n = rd();
	m = rd();
	run();
    return 0;
}

lllllan.赛后代码

#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 1e3 + 10;

int n, m;
int G[N][N];
int vis[N][N];
ll ans, dp[N][N][5];
int dir[][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
inline int min (int a, int b) { return a < b ? a : b;}

inline void dfs (int x, int y) {
	int flag = 1;
	vis[x][y] = 1;
	_for (i, 0, 3) {
		int nx = x + dir[i][0];
		int ny = y + dir[i][1];
		if ((nx < 1 || nx > n || ny < 1 || ny > m) || G[nx][ny] != G[x][y] + 1) continue;
		flag = 0;
		if (!vis[nx][ny]) dfs(nx, ny);	//记忆化搜索 
		_for (k, 1, 4)	//继承序列中下一个点的dp值,注意+1以及上限为4 
			dp[x][y][min(k + 1, 4)] = (dp[x][y][min(k + 1, 4)] + dp[nx][ny][k]) % mod;
	}
	if(flag) dp[x][y][1] = 1;	//flag为1表示为某个序列中的末尾,需要对dp[][][1]赋值为1 
}

int main () {
	scanf("%d%d", &n, &m);
	_for (i, 1, n) _for (j, 1, m) scanf("%d", G[i] + j);
	_for (i, 1, n) _for (j, 1, m) {
			int flag = -1;
			for (int k = 0; k < 4 && flag; k++) {
				int x = i + dir[k][0], y = j + dir[k][1];
				if (x >= 1 && x <= n && y >= 1 && y <= m) {
					if (G[x][y] == G[i][j] + 1) flag = 1;	//周围存在差值为1的数 
					if (G[x][y] == G[i][j] - 1) flag = 0;	//发现不是序列中的最小值取消搜索 
				}
			}
			if (flag == 1) {
				dfs(i, j);
				ans = (ans + dp[i][j][4]) % mod;
			}
		}
	printf("%lld\n", ans % mod);
    return 0;
}

H - Prince and Princess

solved by Sstee1XD. 04:17(+4)

题意:王子要娶公主,他要问(他并不知道他向谁提问)出席婚礼的所有人公主在哪。出席婚礼的人包括支持者(包括公主),反对者,无所谓者。支持者一定说实话,反对者和无所谓者可能说假话,问你王子最少要问几个人能知道公主在哪儿。

题解:注意支持者里包括公主自己,所以我们要特判1 0 0的情况。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;

#define endl "\n"
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () {	cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' '; err(args...);}

inline int rd () {
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;
int a, b, c;
void run () {
	a = rd(), b = rd(), c = rd();
	if (a <= b + c) {
		puts("NO");
	}
	else {
		puts("YES");
		if (a == 1 && b + c == 0) {
			printf("0\n");
		}
		else printf("%d\n", 2 * (b + c) + 1);
	}
}

int main () {
	run();
    return 0;
}

J - Spy

Solved by Sstee1XD. (-)

题意: 给你一个 n n n,接下来给你 4 4 4 n n n列的数字,分别代表敌方各个单位的能量,击败这个敌方单位能获得的点数,己方各个单位的能量以及己方 n n n个装备的能量。对于这 n n n个装备你可以装在任意一个己方单位上,并把能量加在对应的单位上。己方的一个单位会和一个敌方单位对上,若己方单位的能量大于敌方单位,即可获得对应的点数,但你并不知道己方单位会和哪个敌方单位对上。现在要求最终的数学期望乘上 n n n

题解: 我们考虑固定己方单位的位置,然后枚举哪个装备装在这个己方单位的身上,再枚举他对上的敌方单位。这时我们发现我们可以把当前对上的敌方单位的位置也固定上,那么当前位置对整体的贡献为能获得的点数乘以 ( n − 1 ) ! (n - 1)! (n1)!。由于答案最终要乘以 n n n,所以概率对答案已经没有影响了。这时我们对于装备的己方单位建边,边的权值为能获得点数,于是这道题目就变成了一道km问题。注意 n n n的数量过大,只能用 n 3 n^3 n3的板子写。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 407;
const ll INF = 0x3f3f3f3f3f3f3f3f;

ll a[maxn], b[maxn], c[maxn], d[maxn];
bool vis[maxn];
ll cap[maxn][maxn];
ll slack[maxn], l[maxn];
ll wx[maxn], wy[maxn], pre[maxn];
int n;

void bfs(int k)
{
    ll px, py = 0, yy = 0, d;
    memset(pre, 0, sizeof(pre));
    memset(slack, 0x3f, sizeof(slack));
    l[py] = k;
    while (l[py]) {
        px = l[py], d = INF, vis[py] = true;
        for(int i = 1;i <= n;i++)
            if(!vis[i])
            {
                if(slack[i] > wx[px] + wy[i] - cap[px][i])
                    slack[i] = wx[px] + wy[i] - cap[px][i], pre[i]=py;
                if(slack[i] < d) d = slack[i], yy = i;
            }
        for(int i = 0;i <= n; i++)
            if(vis[i]) wx[l[i]] -= d,wy[i] += d;
            else slack[i] -= d;
        py = yy;
    }
    while(py) l[py] = l[pre[py]], py = pre[py];
}
ll km(){
    memset(wx,0,sizeof(wx));
    memset(wy,0,sizeof(wy));
    memset(l,0,sizeof(l));
    for(int i = 1;i <= n; i++)
        memset(vis, 0, sizeof(vis)), bfs(i);

    ll ans = 0;
    for (int i = 1; i <= n; i++) ans += cap[l[i]][i];
    return ans;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    cin >> n;
    for (int i=1; i<=n; i++) cin >> a[i];
    for (int i=1; i<=n; i++) cin >> b[i];
    for (int i=1; i<=n; i++) cin >> c[i];
    for (int i=1; i<=n; i++) cin >> d[i];
    for (int i=1; i<=n; i++){
        for (int j=1; j<=n; j++){
            for (int k=1; k<=n; k++){
                if (c[i] + d[j] > a[k]) cap[i][j] += b[k];
            }
        }
    }
    ll ans = km();
    cout << ans << "\n";
    return 0;
}

K - Triangle

solved by Tryna 03:59(+7)

题意: 给出三角形的三个顶点和一条线段的一个端点,问是否存在另外一个点和之前的端点构成一条线段,满足落在三角形边上,并且把三角形分成面积相等的两部分,起始点也需要在三角形边上。

题解: 赛后看了一下大家的bolg,基本上都是用二分写的,我觉得我的写法还挺好的。首先如果起始点不在三角形边上,直接输出 -1,如果起始点在边上,那么就一定能够找到另外一个点也在三角形边上把这个三角形分为面积相等的两部分,接下来我们就需要去找到这个点,这里我用到了高中时常用的求三角形面积公式 S = 1 2 ∗ sin ⁡ θ ∗ a ∗ b S = \frac{1}{2} * \sin\theta * a * b S=21sinθab
在这里插入图片描述
以上面这张图举例,A B C是给出的三角形的三个顶点, P也是给出的,我们想要找到Q,我的方法是:首先找到P所在的直线,然后找到在这条直线上找到离P远的顶点,以这个点为公共角去寻找Q,我们可以通过下面的方程找到BQ的长度 1 2 ∗ sin ⁡   B ∗ B C ∗ B A = 2 ∗ 1 2 ∗ sin ⁡   B ∗ B P ∗ B Q \frac{1}{2} * \sin\ B * BC * BA = 2 * \frac{1}{2} * \sin\ B * BP * BQ 21sin BBCBA=221sin BBPBQ
可以直接化简到 B C ∗ B A = 2 ∗ B P ∗ B Q BC * BA = 2 * BP * BQ BCBA=2BPBQ
因为只有BQ是未知的所以可以容易得到BQ的值,进而就可以根据BA向量的方向来确定Q的坐标了。

代码

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
int sgn(double x){
	if(fabs(x) < eps) return 0;
	else return x < 0 ? -1 : 1;
}
struct Point{
	double x, y;
	Point(){}
	Point(double x, double y):x(x),y(y){}
	Point operator - (Point B){
		return Point(x - B.x, y - B.y);
	}
}P[5], pos[5];

struct Line{
	Point p1, p2;
	Line(){}
	Line(Point p1, Point p2):p1(p1),p2(p2){}
}L[5];

double Dist(Point A, Point B){
	return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}

double Dot(Point A, Point B){
	return A.x * B.x + A.y * B.y;
}

double Cross(Point A, Point B){
	return A.x * B.y - A.y * B.x;
}

bool Point_on_seg(Point p, Line v){
	return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 &&
	sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}

int t;
int main () {
	scanf("%d", &t);
	while(t--){
		for(int i = 1; i <= 4; i++) scanf("%lf %lf", &P[i].x, &P[i].y);
		L[1] = Line(P[1], P[2]);
		L[2] = Line(P[2], P[3]);
		L[3] = Line(P[3], P[1]);
		if(Point_on_seg(P[4], L[1]) == 0 && Point_on_seg(P[4], L[2]) == 0 && Point_on_seg(P[4], L[3]) == 0){
			printf("-1\n");
			continue;
		}
		int p = 0;
		for(int i = 1; i <= 3; i++){
			if(Point_on_seg(P[4], L[i]) == 1){
				p = i;
				break;
			}
		}
		if(p == 1)	pos[1] = P[3];	
		else if(p == 2) pos[1] = P[1];
		else pos[1] = P[2];
		if(Dist(P[4], L[p].p1) > Dist(P[4], L[p].p2)){
			pos[2] = L[p].p1;
			pos[3] = L[p].p2;
		} 
		else{
			pos[2] = L[p].p2;	
			pos[3] = L[p].p1;
		} 
		double cnt = Dist(P[4], pos[2]) / Dist(pos[2], pos[3]);
		cnt = 0.5 / cnt;
		double resultX = cnt * (pos[1].x - pos[2].x) + pos[2].x;
		double resultY = cnt * (pos[1].y - pos[2].y) + pos[2].y;
		printf("%.12f %.12f\n", resultX, resultY);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞赛)是一项面向大学生的计算机编程竞赛,涉及算法和数据结构等领域。在比赛中,选手需要解决一系列编程问题,使用合适的算法和数据结构来实现正确和高效的解决方案。 对于整理ACM-ICPC模板,以下是一些建议: 1. 了解比赛要求:首先,你需要了解ACM-ICPC比赛的具体要求和规则。这包括了解比赛所涉及的算法和数据结构,以及题目的类型和难度等。 2. 收集资料:收集与ACM-ICPC相关的资料,包括经典算法和数据结构的实现代码、常见问题的解题思路等。可以参考教材、博客、论文等资源。 3. 整理模板:将收集到的资料整理成模板。可以按照算法和数据结构的分类进行整理,例如排序算法、图算法、字符串算法等。对每个模板,添加必要的注释和示例代码,以便理解和使用。 4. 测试代码:对每个模板编写测试代码,确保它们的正确性和可靠性。可以使用已知的测试用例或自行设计测试用例。 5. 更新与扩充:定期更新和扩充模板,以适应ACM-ICPC比赛中新出现的算法和数据结构。同时,根据自己的经验和理解,对模板进行优化和改进。 6. 练习和复习:在比赛之前,利用整理好的模板进行练习和复习。尝试解决一些经典问题,使用模板中的算法和数据结构进行实现,并进行优化。 希望这些建议对你整理ACM-ICPC模板有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值