2020 ICPC 沈阳站

牛客重现赛 Codeforces

C Mean Streets of Gadgetzan

Description

\(n\) 个命题, \(m\) 个事件作为命题可能的条件。命题有以下四种:

  1. \(x\) 表示\(x\)确定为真
  2. \(!x\) 表示\(x\)确定为假
  3. $a_1\ a_2\ ...\ a_k\ $ -> \(x\) 表示:如果\(a_i(i\in[1,k])\)都为真,则 \(x\)为真
  4. $a_1\ a_2\ ... a_k $ -> \(!x\) 表示:如果\(a_i(i\in[1,k])\)都为真,则\(x\)为假

构造出\(m\)个条件的真假使得这 \(n\) 个命题不互相矛盾,并输出\(m\)个字符来表示\(m\)个条件的真假;

如果构造不出来,即 \(n\) 个命题互相矛盾,则输出“conflict”

Solution

注意到对于后面两个类型的命题,只要存在一个 \(a_i\) 为假,则无论 \(x\) 取何值命题都为真。

初始将所有条件默认为\(0\),标记为\(-1\) (为了区分原始赋的 \(0\) 和确定下来为 \(0\) 这两种情况)。

对于前面两个类型的命题,能够直接确定条件的真假,将值为 \(1\) 的条件加入队列 (注意至多进队一次);

对于后面两个类型的命题,用结构体 \(b\) 来记录信息,\(bnum\) 为当前命题记录在结构体中的编号(结构体的下标),\(vector<int>\)类型的 \(ind[t]\) 记录 \(t\) 作为 -> 前的一个\(a_i\) 的命题编号,\(b\) 中存三个信息,①是命题包含的 \(a_i\) 个数 \(num0\)(实际记录没有置为 \(1\)\(a_i\) 数目,初始为 \(a_i\) 总数目),②是命题中 -> 后的 \(x\) ,③是命题的类型 \(0\) 表示若 \(a_i\) 都为真则 \(x\) 为真, \(1\) 表示若 \(a_i\) 都为真则 \(x\) 为假。

每次从队首取出一个元素 \(u\),访问 \(ind[u]\) 所有的值,将命题对应的 \(num0\) 减一,若 \(num0==0\) ,则其对应的 \(x\) 值可以确定下来,若 \(x\) 第一次被置为 \(1\) ,则加入队列。

在操作的过程中出现任何矛盾都直接输出 \(conflict\) 并结束程序。

注:说明中的实现和程序并不完全一样,但意义和逻辑相同。

Code

//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
using namespace std;
const int N = 1e6 + 5;
int n, m, col[N], anum, a[N][2];
bool atype[N], vis[N];
vector<int> ind[N];
queue<int>q;
string s;
void END() {
	printf("conflict\n");
	exit(0);
}
int main() {
	cin >> n >> m;
	getchar();
	for (int i = 1;i <= m;++i) col[i] = -1;
	for (int i = 1;i <= n;++i) {
		getline(cin, s); //整行输入(不包括末尾换行符)
		if (s.find("->") == -1) {
			if (s[0] != '!') {
				int x = stoi(s);
				if (col[x] != -1) END();
				col[x] = 1;
				if(!vis[x]) q.push(x), vis[x]=1;
			}
			else {
				int x = stoi(s.substr(1));
				if (col[x] != -1) END();
				col[x] = 0;
			}
		}
		else {
			int pos = s.find("->"), anum_tmp = 0;
			++anum;
			string tmps = "";
			for (int j = 0;j < pos;++j)
				if (s[j] != ' ') tmps += s[j];
				else ind[stoi(tmps)].push_back(anum), tmps = "", ++anum_tmp;
			atype[anum] = (s[pos + 3] != '!');
			int x = stoi(s.substr(pos + 3 + (s[pos + 3] == '!')));
			a[anum][0] = x;
			a[anum][1] = anum_tmp;
		}
	}
	while (q.size()) {
		int u = q.front();q.pop();
		for (int i = 0;i < ind[u].size();++i) {
			int t = ind[u][i], x = a[t][0];
			--a[t][1];
			if (!a[t][1]) {
				if (atype[t]) {
					if (col[x] == 0) END();
					col[x] = 1;
					if (!vis[x]) q.push(x), vis[x] = 1;
				}
				else {
					if(col[x] == 1) END();
					col[x] = 0;
				}
			}
		}
	}
	for (int i = 1;i <= m;++i)
		if (col[i] == -1 || col[i] == 0) printf("F");
		else printf("T");
	return 0;
}

D-Journey to Un'Goro

Description

​ 构造一个长度为 \(n\) 的序列,其组成元素只能是 \(b\)\(r\) ,区间 \([i,j]\) 是一段“好”的区间当且仅当该区间内的 \(r\) 有奇数个。

​ 首先输出长度为 \(n\) 的序列最多有多少个 “好” 的区间,然后按照字典序输出“好”的区间数最多的序列,若这样的序列超过100个,则只需输出按字典序排序后的前100个。

Solution

​ 容易想到,当序列元素全为 \(r\) 时,达到最大答案:

​ 若 \(n\) 为奇数,\(ans=n+(n-2)+(n-4)+...+1=\displaystyle \frac{(n+1)^2}{4}\)

​ 若 \(n\) 为偶数,\(ans=n+(n-2)+(n-4)+...+2=\displaystyle \frac{n(n+2)}{4}\)

​ 由于最小只需要输出\(100\)个序列,考虑搜索+剪枝。

​ 尝试多找到一些性质来剪枝。

​ 设\(S[i]\)表示序列的前\(i\)个元素中\(r\)的个数,则一个区间\([i,j]\)好,当且仅当\(S[j]-S[i-1]\)为奇数 \(\Leftrightarrow\) \(S[j]\)\(S[i-1]\)的奇偶性不同。

​ 若\(S[1]...S[n]\)中共有\(X\)个奇数,\(Y\)个偶数,则答案为\(XY\)\(XY=X(n-X)=-X^2+nX=-(X-\displaystyle \frac{n}{2})^2+\displaystyle \frac{n^2}{4}\)

​ 当且仅当\(X==Y\)\(|X-Y|==1\)时,\(XY\)取得最大值。

Code

//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
int n, totr[2], totbr[2], totans, maxr;
ll ans;
char seq[N];
void dfs(int cur, int totr1, int totr2, bool flag) {  
	//totr1  前缀r数目为奇数的个数 totr2 前缀r数目为偶数的个数 flag=0 当前前缀r数目为偶 flag=1 当前前缀r数目为奇
	if (totr1 > maxr || totr2 > maxr) return;
	if (cur == n) {
		++totans;
		seq[n] = '\0';
		printf("%s\n", seq);
		if (totans == 100) exit(0);
		return;
	}

	seq[cur] = 'b';
	dfs(cur + 1, totr1 + flag, totr2 + (flag ^ 1), flag);  //注意(flag^1)处要打括号

	seq[cur] = 'r';
	dfs(cur + 1, totr1 + (flag ^ 1), totr2 + flag, flag ^ 1);

}
int main() {
	cin >> n;
	if (n % 2) ans = 1ll * (n + 1) * (n + 1) / 4;
	else ans = 1ll * n * (n + 2) / 4;
	cout << ans << endl;
	maxr = (n + 2) / 2;
	dfs(0, 0, 1, 0);
	return 0;
}

F Kobolds and Catacombs 思维

Description

What's the maximum number of consecutive groups they can be partitioned into, such that after reordering the kobolds in each group in non-descending order, the entire queue is non-descending?

给定一个序列,问该序列最多能划分为多少个区间,满足:对每个区间内排序后,整个序列是不下降的。

Solution

思路:将序列排序,比较、观察原序列(\(a\))与排序后序列(\(b\))。

注意到对于下标 \(i\) ,若 \(a\) 数组与 \(b\) 数组的前 \(i\) 个数相同, 就在 \(i\) 后面将序列划开。可以得到最大区间数目。

思考用什么特征值来判断前 \(i\) 个数是否相同。

可以证明 前缀和可以作为这个特征值。

简单证一下:

假设前缀和不足以作为该特征值,即 \(\exist \ suma_i=sumb_i\)\(\{a_1,..,a_i\}\ne \{b_1,...,b_i\}\)

\(i=2\)时,

\(a_1+a_2=b_1+b_2\)\(\{a_1,a_2\}\ne\{b_1,b_2\}\)

假设\(a_1<a_2\),则一定有 \(b_1<a_1<b_2<a_2\ or\ a_1<b_1<a_2<b_2\),那么对 \(a\) 序列排序后,\(b_1,b_2\) 之间一定还有一个$a_1 \(或\)a_2$ 。则假设不成立。

\(i>2\)的情况类似。

Code

//by DTTTTTTT
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 5;
int n, a[N], b[N], ans;
ll suma[N], sumb[N];
int main() {
	cin >> n;
	for (int i = 1;i <= n;++i) cin >> a[i], b[i]=a[i];
	sort(b + 1, b + n + 1);
	for (int i = 1;i <= n;++i) {
		suma[i] = suma[i - 1] + a[i];
		sumb[i] = sumb[i - 1] + b[i];
		if (suma[i] == sumb[i]) ++ans;
	}
	cout << ans << endl;
	return 0;
}

G The Witchwood 签到题

Description

\(n\) 个物品,第 \(i\) 个物品价值 \(a_i\) ,最多取 \(k\) 个物品,求最大价值。

Solution

签到题。 对所有 \(a_i\) 从大到小排序,取前 \(k\) 个加起来就是答案。

H The Boomsday Project DP+二分

Description

\(n\) 种优惠券,第 \(i\) 种优惠券的有效时间为 \(d[i]\) 天,可以免费借用 \(k[i]\) 次自行车,价格为 \(c[i]\)

\(Aloha\) 会借用自行车 \(m\) 天,在第 \(p[i]\) 天,他会借用 \(q[i]\) 次自行车。

如果不使用优惠券,单次借用自行车的价格为 \(r\) 元。

\(Aloha\) 的最小费用。

Solution

长得就像动态规划的样子?

\(f[i]\) 为前 \(i\) 次借用自行车的最小花费。

外层循环枚举前 \(i\) 次借用,内层循环枚举第 \(j\) 种优惠券。

转移:找到 \(ind\) 满足:第 \(ind+1...i\) 次借用都可以使用当前枚举的第 \(j\) 种优惠券

\(f[i]=min(f[i],f[ind]+c[j])\)

初始条件:\(f[0]=0\) \(f[i]=inf(i=1,2,...,\sum q[i])\)

细节还是挺多的,,以及,,重构代码解决 \(dtt\ 99\%\)的问题!

Code

//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 505, M = 1e5 + 5, Q = 3e5 + 5;
const ll inf = 1e18;
int n, m, r, d[N], k[N], c[N], sumq[M], totq, days[Q];
ll f[Q];
struct days {
	int p, q;
}a[M];
bool cmp(struct days x, struct days y) {
	return x.p < y.p;
}
int ef(int cur, int limit) {
	int l = 1, r = m, mid, ret = 1;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (a[mid].p >= cur - limit + 1) ret = mid, r = mid - 1;
		else l = mid + 1;
	}
	return ret;
}
int main() {
	scanf("%d%d%d", &n, &m, &r);  //n种优惠券 m天 单次借用花费r元
	for (int i = 1;i <= n;++i) scanf("%d%d%d", &d[i], &k[i], &c[i]); //d:天数限制 k:次数限制 c:价格
	for (int i = 1;i <= m;++i) scanf("%d%d", &a[i].p, &a[i].q); //第p天借用q次

	sort(a + 1, a + m + 1, cmp);
	for (int i = 1;i <= m;++i) {
		sumq[i] = sumq[i - 1] + a[i].q;
		for (int j = 1;j <= a[i].q;++j) {
			int cur = sumq[i - 1] + j;
			f[cur] = inf;
			days[cur] = a[i].p;
		}
	}
	totq = sumq[m];
	d[0] = k[0] = 1, c[0] = r;

	for (int i = 1;i <= totq;++i) {
		for (int j = 0;j <= n;++j) {
			int ind;
			if (days[max(i - k[j] + 1, 1)] >= days[i] - d[j] + 1) ind = max(i - k[j], 0);
			else ind = sumq[ef(days[i], d[j]) - 1];
			f[i] = min(f[i], f[ind] + c[j]);
		}
	}
	printf("%lld\n", f[totq]);
	return 0;
}

I Rise of Shadows 数学/同余

Description

\(Azeroth\) 的时间设置如下:一天 \(H\) 个小时,一小时 \(M\) 分钟。

给定一个\(Azeroth\)的时钟,问一天之内有多少整数分钟时刻满足个分针和时针夹角不超过 \(\alpha\) ($\alpha =\displaystyle\frac{2\pi \times A }{H\times M} $)。

已知新的一天开始时,时针和分针重合。

Solution

时针的每分钟旋转的角度: \(\displaystyle \frac{2\pi}{H\times M}\)

分针每分钟旋转的角度:\(\displaystyle \frac{2\pi}{M}\)

计入答案的整数分钟时刻 \(t\) 应该满足:

\(2\pi k-\alpha\leq(\displaystyle \frac{2\pi}{M}-\displaystyle \frac{2\pi}{H\times M})\times t \leq 2\pi k+\alpha\ \ (k=0,1,...)\)

\(H\times M\times k-\displaystyle \frac{\alpha \times H\times M}{2\pi}\leq(H-1)\times t\leq H\times M\times k+\displaystyle \frac{\alpha \times H\times M}{2\pi}\ \ (k=0,1,...)\)

又$\because \alpha =\displaystyle\frac{2\pi \times A }{H\times M} $

\(\therefore H\times M\times k-A\leq(H-1)\times t\leq H\times M\times k+A\ \ (k=0,1,...)\)

\((H-1)\times t \leq A\ (mod\ HM)\)\((H-1)\times t \geq HM-A\ (mod\ HM)\)

等价于 $(H-1)\times t\equiv K(mod\ HM) $ 其中 \(K\leq A\)\(K\ge HM-A\)

线性同余方程 \(a*x\equiv b(mod\ m)\) 有解当且仅当 \(gcd(a,m)|b\)

在有解时,先用欧几里得算法求出一组整数\(x_0,y_0\),满足 \(a*x_0+m*y_0=gcd(a,m)\),则\(x=x_0*b/gcd(a,m)\)是该线性方程的一个解。

\(Bezout\)定理可知:\(gcd(a,m)=1\)时,\(a*x\equiv b(mod\ m)\)\(x\in[0,m-1]\) 上有唯一解。

同样地,\(a/gcd(a,m)\)\(m/gcd(a,m)\)互质,则\(\displaystyle\frac{a}{gcd(a,m)}*x\equiv \displaystyle\frac{b}{gcd(a,m)}\ (mod\ \displaystyle\frac{m}{gcd(a,m)})\)\(x\in[0,\displaystyle\frac{m}{gcd(a,m)}-1]\)上有唯一解。

为了满足\(gcd(a,m)=1\),将等式$(H-1)\times t\equiv K(mod\ HM) $ 其中 \(K\leq A\)\(K\ge HM-A\) 两边同时除以\(gcd(H-1,HM)\)得:

$\displaystyle\frac{H-1}{gcd(H-1,HM)}\times t\equiv \displaystyle\frac{K}{gcd(H-1,HM)}(mod\ \displaystyle\frac{HM}{gcd(H-1,HM)}) $

\(Bezout\)定理可知:由于\(\displaystyle\frac{H-1}{gcd(H-1,HM)}\)\(\displaystyle\frac{HM}{gcd(H-1,HM)}\)互质,\(\displaystyle\frac{H-1}{gcd(H-1,HM)}\)\(\times t\equiv \displaystyle\frac{K}{gcd(H-1,HM)}\)\((mod\ \displaystyle\frac{HM}{gcd(H-1,HM)})\)\(x\in\)\([0,\displaystyle\frac{HM}{gcd(H-1,HM)}-1]\)上有唯一解,在\(x\in[\displaystyle\frac{HM}{gcd(H-1,HM)},\displaystyle\frac{2HM}{gcd(H-1,HM)}-1]\)上有唯一解..........

\(\displaystyle\frac{H-1}{gcd(H-1,HM)}\times t\equiv \displaystyle\frac{K}{gcd(H-1,HM)}(mod\ \displaystyle\frac{HM}{gcd(H-1,HM)})\)\([0,HM-1]\)上有\(gcd(H-1,HM)\)个解。

即:对于每一个有解的\(K\) (即:满足 \(gcd(H-1,HM)|K\)),有\(gcd(H-1,HM)\)个合法的\(t\)

而有解的\(K\)的数目为:\((\lfloor \displaystyle \frac{A}{gcd(H-1,HM)}\rfloor+1)+(\displaystyle \frac{HM}{gcd(H-1,HM)}-\lceil \displaystyle \frac{HM-A}{gcd(H-1,HM)}\rceil)\)

所以答案为:

\((\lfloor \displaystyle \frac{A}{gcd(H-1,HM)}\rfloor+1)+(\displaystyle \frac{HM}{gcd(H-1,HM)}-\lceil \displaystyle \frac{HM-A}{gcd(H-1,HM)}\rceil)*gcd(H-1,HM)\)

注意到当\(A=\displaystyle\frac{HM}{2}\) 时,\(A=HM-A\),按照 \(K\leq A\)\(K\ge HM-A\) 求得\(K\)的数目可能会多一个,需要特殊判断,易知这种情况的答案为\(HM\)

Code

//by DTTTTTTT
#include<iostream>
#define ll long long
using namespace std;
ll H, M, A;
ll gcd(ll x, ll y) {
	return y == 0 ? x : gcd(y, x % y);
}
int main() {
	cin >> H >> M >> A;
	if (A * 2 == H * M) {
		cout << A * 2 << endl;
		return 0;
	}
	ll d = gcd(H - 1, H * M);
	cout << d * (A / d + 1 + H * M / d - (H * M - A + d - 1) / d) << endl;
	return 0;
}

K Scholomance Academy

Description

posted @ 2022-10-16 20:40  DTTTTTTT-  阅读(167)  评论(0)    收藏  举报