2020 HDU Multi-University Training Contest 3(部分)

6794 Tokitsukaze and Multiple

题目就是求最多有几个连续区间之和为p的倍数,换个角度看问题就是,把是p的倍数的区间都列出来,当成一条条线段,求不相交线段最多可选几条。用贪心法,按照线段的右端点排序,从左到右能选就选。如果与之前的线段发生了冲突,则必定不选,因为如果选了,至少使得前面的这条线段会删去,而后续可选的线段也有变少的可能性。
用map来维护前缀和,记录每次前缀和的数值%p,如果再次遇到相同的数值,说明正好横跨一个p的长度,也就是目前这个元素的位置能作为一个p倍数线段的右端点,答案+1,同时要清除map并且再次从0开始求前缀和(之前的点对后面无影响_

#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int T, n, p;
ll a[maxn], sum[maxn];
int main(void) {
	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &p);
		map<ll, int>mp;
		mp[0] = 1;
		int ans = 0;
		for (int i = 1, sum = 0; i <= n; i++) {
			scanf("%d", &a[i]);
			a[i] %= p;
			sum = (sum + a[i]) % p;
			if (mp.count(sum)) {
				ans++;
				mp.clear();
				mp[0] = 1;
				sum = 0;
			}
			else mp[sum] = 1;
		}
		printf("%d\n", ans);
	}
	return 0;
}

6795 Little W and Contest

每次介绍两个人认识,新集合的贡献不需要额外计算,因为在之前,这两个人所在的集合可以看作一个整体或者看作两个部分,对其它集合的影响都不变。会变得不合法的贡献只有两个集合之间各取一个人的情况,所以删除该不合法的情况即可。

#include<cstdio>
using namespace std;
typedef long long ll;
const ll p = 1e9 + 7;
const int maxn = 1e5 + 10;
int fa[maxn], a, num1[maxn], num2[maxn];
int T, n, u, v;
ll ans, sum2, sum1;
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
    x = find(x); y = find(y);
    ll k = 0;
    k = (k + 1LL * num1[x] * num2[y] * (sum2 - num2[x] - num2[y])) % p;
    k = (k + 1LL * num2[x] * num1[y] * (sum2 - num2[x] - num2[y])) % p;
    k = (k + 1LL * num2[x] * num2[y] * (sum2 - num2[x] - num2[y])) % p;
    k = (k + 1LL * num2[x] * num2[y] * (sum1 - num1[x] - num1[y])) % p;
    ans = (ans - k + p) % p;
    fa[y] = x;
    num1[x] += num1[y]; num2[x] += num2[y];
    num1[y] = 0;  num2[y] = 0;
    printf("%lld\n", ans);
}
inline int read() {
    int x = 0; char c = getchar();
    while (c < '0' || c>'9') c = getchar();
    while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
    return x;
}
inline ll C_2(int n) {
    if (n < 2)return 0;
    return (ll)(n - 1) * n / 2 % p;
}
inline ll C_3(int n) {
    if (n < 3)return 0;
    return (ll)(n - 2) * (n - 1) * n / 6 % p;
}
int main(void) {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    T = read();
    while (T--) {
        n = read();
        sum1 = sum2 = 0;
        for (register int i = 1; i <= n; i++) {
            fa[i] = i; a = read();
            if (a == 1)num1[i] = 1, num2[i] = 0, sum1++;
            else num2[i] = 1, num1[i] = 0, sum2++;
        }
        ans = (C_2(sum2) * sum1 % p + C_3(sum2)) % p;
        printf("%lld\n", ans);
        n--;
        while (n--) {
            u = read(); v = read();
            merge(u, v);
        }
    }
    return 0;
}

6797 Tokitsukaze and Rescue

Dijkstra+DFS,因为是随机的边,所以最短路的边数很少。
DFS有k层每层都枚举一条边即可。

#include<cstdio>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 55;
int T, n, k, g[maxn][maxn];
int u, v, w, pre[maxn], ans;
struct node{
	int id, dis;
	node() {}
	node(int id, int dis) :id(id), dis(dis) {}
	bool operator < (const node& B)const {
		return dis == B.dis ? id < B.id : dis > B.dis;
	}
};
int dijkstra(int cmd = 0) {
	int s = 1;
	int dis[maxn], done[maxn];
	for (int i = 1; i <= n; i++) {
		dis[i] = inf; done[i] = 0;
	}
	dis[s] = 0;
	priority_queue<node>pq;
	pq.push(node(s, 0));
	while (!pq.empty()) {
		node t = pq.top(); pq.pop();
		if (t.id == n)return t.dis;
		if (done[t.id])continue;
		done[t.id] = 1;
		for (int i = 1; i <= n; i++) {
			if (done[i])continue;
			if (t.dis + g[t.id][i] < dis[i]) {
				dis[i] = t.dis + g[t.id][i];
				pq.push(node(i, dis[i]));
				if (!cmd)pre[i] = t.id;
			}
		}
	}
}
void dfs(int k) {
	int t = dijkstra();
	if (k == 0) {
		ans = max(ans, t);
		return;
	}
	int x = n, maxx = 0, set = 0;
	int from[maxn], to[maxn], cnt = 0;
	while (pre[x]){
		from[cnt] = pre[x];
		to[cnt++] = x;
		x = pre[x];
	}
	for (int i = 0; i < cnt; i++) {
		int bef = g[from[i]][to[i]];
		g[from[i]][to[i]] = g[to[i]][from[i]] = inf;
		dfs(k - 1);
		g[from[i]][to[i]] = g[to[i]][from[i]] = bef;
	}
}
int main(void) {
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &k);
		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				scanf("%d%d%d", &u, &v, &w);
				g[u][v] = g[v][u] = w;
			}
		}
		ans = 0;
		dfs(k);
		printf("%d\n", ans);
	}
	return 0;
}

6798 Triangle Collision

平面镜对称铺路,重点是旋转120度,为了保证三角形3条边的方向的贡献都加上。 旋转坐标轴详细请看:https://blog.csdn.net/a6333230/article/details/88343282
每次只考虑平行x轴的方向,向下取整求y/h,如果是负方向,负数浮点数向下取整为-1,因此会贡献自动加1。

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double base = 2 * pi / 3;
int T, k;
double L, sx, sy, vx, vy, h;
double get_dis(double x,double y, double rad){
    return -x * sin(rad) + (y - h / 3) * cos(rad) + h / 3;
}
bool ck(double times){
    long long has = 0;
    has += abs(floor(get_dis(sx + vx * times, sy + vy * times, base * 0) / h));
    has += abs(floor(get_dis(sx + vx * times, sy + vy * times, base * 1) / h));
    has += abs(floor(get_dis(sx + vx * times, sy + vy * times, base * 2) / h));
    return has >= k;
}
int main(void){
    scanf("%d", &T);
    while (T--){
        scanf("%lf %lf %lf %lf %lf %d", &L, &sx, &sy, &vx, &vy, &k);
        h = L * sqrt(3) / 2;
        double l = 0, r = 1e10, mid = (l + r) / 2;
        while (r - l > eps){
            if (ck(mid)) r = mid;
            else l = mid;
            mid = (l + r) / 2;
        }
        printf("%.8lf\n", (l + r) / 2);
    }
    return 0;
}

6799 Parentheses Matching

为了保证字典序,左括号总往最左边能加的地方加,右括号总在最右边能加的地方加,因为原字符串的括号不能动,所以必须加括号使得字符串的括号匹配,从左到右,从右到左都遍历一遍,遇到不平衡就加括号。

#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int maxn = 1e5 + 5;
char s[maxn], ans[maxn];
int T;
int main(void) {
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s + 1);
		int len = strlen(s + 1), flag = 0;
		for (register int i = 1,bef=1, num = 0; i <= len; i++) {
			if (s[i] == '(')num++;
			else if (s[i] == ')')num--;
			if (num < 0)
				for (int j = bef; j < i; j++)
					if (s[j] == '*') {
						s[j] = '(';
						bef = j + 1;
						num++;
						break;
					}
			if (num < 0) {
				flag = 1;
				break;
			}
		}
		for (register int i = len, bef = len, num = 0; i; i--) {
			if (s[i] == ')')num++;
			else if (s[i] == '(')num--;
			if (num < 0)
				for (int j = bef; j > i; j--)
					if (s[j] == '*') {
						s[j] = ')';
						bef = j - 1;
						num++;
						break;
					}
			if (num < 0) {
				flag = 1;
				break;
			}
		}
		if (flag)printf("No solution!");
		else for (int i = 1; i <= len; i++)
			if (s[i] != '*')printf("%c", s[i]);
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值