2020杭电多校第三场

hdu-6794 Tokitsukaze and Multiple

题意:给n个数和一个模数p,现在需要你对这n个数中的若干个连续的数进行合并,最后要使得这个序列中尽可能多的数能够被p整除,输出最大个数

思路:如果这个数本身就是p,那么我们就不需要让它跟其他数进行合并,因为再怎么合并还是算1个,划不来。然后就是其他的数了,我们对不是p的数进行求前缀和并且取模,如果前缀和相同的话那么就是出现了可以被p整除的组合。

比如说一个序列 1 4 2 ;p 为 3,这个序列的前缀和就是 1 2 1,那么就可以知道第二项和第三项是可以组成能够整除3的数的,这个需要用vis数组来存一下前面出现过的数就可以了。还有需要注意的一点就是当一个方案产生的时候,vis数组需要清空,因为必须是连续的数才可以合并。

#include <bits/stdc++.h>
using namespace std;

int _, n, p, a, sum[100005];
map <int, bool> vis;

int main() {
	scanf("%d", &_);
	while(_--) {
		vis.clear();
		int ans = 0;
		scanf("%d %d", &n, &p);
		for(int i = 1;i <= n; i++) {
			scanf("%d", &a); a %= p;
			if(a == 0) {
				vis.clear();
				sum[i] = 0;
				ans++;
				continue;
			}
			sum[i] = (sum[i-1]+a)%p;
			if(vis[sum[i]] || sum[i] == 0) {
				ans++;
				vis.clear();
				sum[i] = 0;
			}
			else vis[sum[i]] = 1;
		}
		
		printf("%d\n", ans);
	}
}

hdu-6795 Little W and Contest

题意:给n个人,每个人的能力值要么是1,要么是2,现在选三个人组队,要求就是

1、团队实力之和 ≥ 5

2、团队之间的人互不熟悉

会有n-1次的两两人之间的介绍,介绍之后两人就会变得熟悉,并且与这两个人熟悉的人也都会互相熟悉。现在需要输出 [ 0 , n − 1 ] [0,n-1] [0,n1] 次介绍之后可以组队的方案数。

思路:最开始互不认识的方案数我们是很好求出来的,然后我们可以考虑两个群体合并之后会对答案产生什么影响。假如x集合与y集合合并,是不是就是影响了从x里面拿一个人,并且从y里面拿出一个人,再从出了这两个集合外拿出一个人的情况,然后我们把能力值不小于5这个因素考虑进来就有以下四种情况可以从原答案中减去。用并查集来维护集合的关系。

image-20200730102923316
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int _, n, x, u, v, fa[100005];
struct node {
	long long one;
	long long two;
	long long tot;
} acm[100005];
long long fac[100005];
long long qpow(long long x, long long n) {
	long long res = 1;
	for (; n; n >>= 1, x = x * x % mod) 
		if (n & 1) res = res * x % mod; 
	return res;
}
long long inv(long long a) {
	return qpow(a, mod-2)%mod;
}
void solve() {
	fac[0] = 1;
	for(int i = 1;i <= 100005; i++) {
		fac[i] = (fac[i-1]*i)%mod;
	}
}
long long comb(long long n, long long k) {
	if(k > n) return 0;
	if(k == 1) return n;
	return (fac[n]*inv(fac[k])%mod * inv(fac[n-k])%mod);
}
int find(int x) {
	if(fa[x] == x) return x;
	else return fa[x] = find(fa[x]);
}

int main() {
	solve();
	scanf("%d", &_);
	while(_--) {
		long long num1 = 0, num2 = 0;
		scanf("%d", &n);
		for(int i = 1;i <= n; i++) fa[i] = i;
		for(int i = 1;i <= n; i++) {
			scanf("%d", &x);
			node t;
			if(x == 1) {
				t.one = 1; t.two = 0; 
				num1++;
			} 
			if(x == 2) {
				t.one = 0; t.two = 1;
				num2++;
			}
			t.tot = t.one + t.two;
			acm[i] = t;
		}
		
		long long ans = ((comb(num2, 2)*comb(num1, 1))%mod+comb(num2, 3))%mod;
		long long res = 0;
		printf("%lld\n", ans);
		for(int i = 0;i < n-1; i++) {
			scanf("%d %d", &u, &v);
			int x = find(u), y = find(v);
			if(x == y) continue;
			long long one1 = acm[x].one, one2 = acm[y].one, two1 = acm[x].two, two2 = acm[y].two;
			res = (res+ comb(two1, 1) * comb(two2, 1)%mod * comb(num2-two1-two2, 1))%mod;
			res = (res+ comb(two1, 1) * comb(two2, 1)%mod * comb(num1-one1-one2, 1))%mod;
			res = (res+ comb(two1, 1) * comb(one2, 1)%mod * comb(num2-two1-two2, 1))%mod;
			res = (res+ comb(one1, 1) * comb(two2, 1)%mod * comb(num2-two1-two2, 1))%mod;
			acm[y].one += acm[x].one;
			acm[y].two += acm[x].two;
			acm[y].tot = acm[y].one + acm[y].two;
			fa[x] = y;
			printf("%lld\n", (ans-res+mod)%mod);
		}
	}
}

hdu-6799 Parentheses Matching

题意:给一个只含有 “(”, “)”, “*” 的字符串现在你需要进行一下操作将这个字符串变得符合以下要求:

括号互相匹配
长度最短
字典序最小

操作为

* -> (
* -> )
* -> 空气

最后输出修改后的字符串

思路:需要保证字符串最短的情况下字典序最小,就不需要考虑在括号已经匹配的情况下将 ∗ * 变成括号的情况了,那么我们可以用栈来维护这个括号序列,如果为 ‘)’ ,并且栈不为空那么就形成一个匹配,栈弹出即可。如果栈为空的话证明‘)’是不匹配的,这时候需要一个容器q来装 ∗ * 的位置,如果q不为空,那么证明这个前面有 ∗ * ,就把q的最前面(保证字典序)的 ∗ * 替换成’(’ 就可以匹配了,如果q仍然为空,那么就不可能满足要求;如果是’(’,直接压入栈,如果是 ∗ * ,就直接加入q。

之后就看栈中有多少个’(’ ,然后从q中最后面的 ∗ * 看(保证字典序),如果这个 ∗ * 的位置在括号后面,那么就可以进行匹配,否则就无法满足要求。

#include <bits/stdc++.h>
using namespace std;

int _;
char s[100007];
deque <int> q;
stack <int> sta;

int main() {
	scanf("%d", &_);
	while(_--) {
		while(!q.empty()) q.pop_back();
		while(!sta.empty()) sta.pop();
		scanf("%s", s);
		bool ok = 1;
		int len = strlen(s);
		for(int i = 0;i < len; i++) {
			if(s[i] == '(') sta.push(i);
			else if(s[i] == ')') {
				if(!sta.empty()) {
					sta.pop();
				}
				else if(!q.empty()) {
					s[q.front()] = '(';
					q.pop_front();
				}
				else {
					ok = 0;
					break;
				}
			}
			else {
				q.push_back(i);
			}
		}
		
		while(!sta.empty()) {
			if(!q.empty() && q.back() > sta.top()) {
				s[q.back()] = ')';
				sta.pop();
				q.pop_back();
			}
			else {
				ok = 0;
				break;
			}
		}
		
		if(ok) {
			for(int i = 0;i < len; i++) {
				if(s[i] != '*') printf("%c", s[i]);
			}
			puts("");
		}
		else puts("No solution!");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值