队内训练(一)

Fibonacci Sum-HDU 6755

传送门
思路

  • 对着式子瞎弄一顿,最后把通项公式套进去发现 a n s = ∑ i = 0 n 1 5 × [ ( 1 + 5 2 ) i c − ( 1 − 5 2 ) i c ] k = 1 5 ∑ i = 0 n ( [ 1 + 5 2 ] i c + [ − ( 1 − 5 2 ) i c ] ) k ans=\sum_{i=0}^{n}\frac{1}{\sqrt{5}}×[(\frac{1+\sqrt{5}}{2})^{ic}-(\frac{1-\sqrt{5}}{2})^{ic}]^k\\=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}([\frac{1+\sqrt{5}}{2}]^{ic}+[-(\frac{1-\sqrt{5}}{2})^{ic}])^k ans=i=0n5 1×[(21+5 )ic(215 )ic]k=5 1i=0n([21+5 ]ic+[(215 )ic])k然后对这个式子进行二项式定理拆分设 A = ( 1 + 5 2 ) c , B = ( 1 − 5 2 ) c A=(\frac{1+\sqrt{5}}{2})^{c},B=(\frac{1-\sqrt{5}}{2})^{c} A=(21+5 )c,B=(215 )c a n s = 1 5 ∑ i = 0 n [ A i + ( − B i ) ] k = 1 5 ∑ i = 0 n ∑ j = 0 k C k j A i j ( − 1 ) k − j B i ( k − j ) 更 换 和 式 位 置 = 1 5 ∑ j = 0 k C k j ( − 1 ) k − j ∑ i = 0 n A j i B ( k − j ) i ans=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}[A^{i}+(-B^i)]^k\\=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}\sum_{j=0}^{k}C^j_kA^{ij}(-1)^{k-j}B^{i(k-j)}\\更换和式位置=\frac{1}{\sqrt{5}}\sum_{j=0}^{k}C^j_k(-1)^{k-j}\sum_{i=0}^{n}A^{ji}B^{(k-j)i} ans=5 1i=0n[Ai+(Bi)]k=5 1i=0nj=0kCkjAij(1)kjBi(kj)=5 1j=0kCkj(1)kji=0nAjiB(kj)i不难发现更换和式后那是一个普通的等比数列,而且按照时间来算的话枚举k个数是十分合理的,正解公式就出来了
  • 这题的时间卡得很紧,对计算组合数求模需要谨慎,另外对每一个k的 A j , B k − j 的 计 算 需 要 O ( 1 ) 处 理 , 避 免 超 时 A^j,B^{k-j}的计算需要O(1)处理,避免超时 Aj,BkjO(1)
  • 在跑代码前先另写个代码跑出 5 \sqrt{5} 5 m o d mod mod下的数字 383008016 383008016 383008016, 1 5 \frac{1}{\sqrt{5}} 5 1 m o d mod mod的数字 276601605 276601605 276601605
  • 然后跑出 A = ( 1 + 5 2 ) c = 691504013 , B = ( 1 − 5 2 ) c = 308495997 A=(\frac{1+\sqrt{5}}{2})^{c}=691504013,B=(\frac{1-\sqrt{5}}{2})^{c}=308495997 A=(21+5 )c=691504013,B=(215 )c=308495997
  • 剩下的按公式来算即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int mod = 1e9 + 9;//383008016,276601605
const int N = 1e5 + 10;
const ll Num = 276601605;
ll fp(ll x, ll y) {
    
    
	ll ans = 1;
	while (y) {
    
    
		if (y & 1) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}
ll fac[N], ifac[N];
ll C(int n, int k) {
    
    
	ll ans = fac[n] * ifac[n - k] % mod * ifac[k] % mod;
	return ans;
}
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	fac[0] = 1;
	for (int i = 1; i <= 1e5; ++i)	fac[i] = fac[i - 1] * i % mod;
	ifac[N - 10] = fp(fac[N - 10], mod - 2);
	for (int i = 1e5 - 1; i >= 0; --i) ifac[i] = (i + 1) * ifac[i + 1] % mod;
	int T; read(T);
	const ll _A = 691504013, _B = 308495997;
	while (T--) {
    
    
		ll n, c; int k;
		read(n); read(c); read(k);
		c = c % (mod - 1);
		const ll A = fp(_A, c), B = fp(_B, c);
		ll a = 1, b = fp(B, k);
		ll _b = fp(B, mod - 2);
		ll ans = 0;
		for (int j = 0; j <= k; ++j) {
    
    
			ll tmp = a * b % mod;
			ll Sn;
			if (tmp == 1) {
    
    
				Sn = (n + 1) % mod;
			}
			else {
    
    
				Sn = (fp(tmp, (n + 1) % (mod - 1)) - 1 + mod) % mod * fp((tmp - 1 + mod) % mod, mod - 2) % mod;
			}
			if ((k - j) & 1) {
    
    
				ans = ((ans - Sn * C(k, j) % mod) % mod + mod) % mod;
			}
			else {
    
    
				ans = (ans + Sn * C(k, j) % mod) % mod;
			}
			a = a * A % mod;
			b = b * _b % mod;
		}
		ans = ans * fp(Num, k) % mod;
		printf("%lld\n", ans);
	}
	return 0;
}

Leading Robots-HDU 6759

传送门
思路

  • 这道题意思是给定加速度与初始位置,判断有多少人有机会拿到第一名
  • 非常经典的单调栈问题,首先根据加速度进行从小到大排序,加速度相同就按照位移从小到大排序,原因下文会阐述
  • 首先将排好的第一个压入栈中,进行下一个操作时,先进行第一步判断:设栈顶的 r o b o t   P robot\ P robot P,此刻判断的 r o b o t   Q robot\ Q robot Q,已知 a P < = a Q a_P<=a_Q aP<=aQ,如果位置 S P < = S Q S_P<=S_Q SP<=SQ,直接可以判断Q点先于P点拿到第一名并且P点不可能超过Q点,所以直接将P点排除
  • 当栈内已经有两个元素时,假设栈顶起第二个元素 r o b o t   X robot\ X robot X,第一个元素 r o b o t   Y robot\ Y robot Y,此刻判断的 r o b o t   Z robot\ Z robot Z,设 Y Y Y追上 X X X所需的时间为 t 1 t_1 t1,(由上一个判断可知 Y Y Y X X X之后), Z Z Z追上 Y Y Y的时间 t 2 t_2 t2,可知只有当 t 1 < t 2 t1<t2 t1<t2 Y Y Y才有可能拿到第一名,否则将 Y Y Y排除
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 5e4 + 10;
struct node {
    
    
	int p, a;
	bool friend operator<(const node& a, const node& b) {
    
    
		if (a.a == b.a) return a.p < b.p;
		return a.a < b.a;
	}
}a[N];
bool check(int x, int y, int z) {
    
    
	return 1LL * (a[x].p - a[y].p) * (a[z].a - a[y].a) >= 1LL * (a[y].a - a[x].a) * (a[y].p - a[z].p);
}
map<pii, int>vis;
int stk[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		vis.clear();
		int n; read(n);
		for (int i = 1; i <= n; ++i) {
    
    
			read(a[i].p); read(a[i].a);
			++vis[{
    
    a[i].a, a[i].p}];
		}
		sort(a + 1, a + 1 + n);
		int top = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			while (top && (a[stk[top - 1]].p <= a[i].p) || top > 1 && check(stk[top - 2], stk[top - 1], i)) --top;
			stk[top++] = i;
		}
		int ans = top;
		for (int i = 0; i < top; ++i) {
    
    
			if (vis[{
    
    a[stk[i]].a, a[stk[i]].p}] > 1) --ans;
		}
		printf("%d\n", ans);
	}
	return 0;
}

Finding a MEX-HDU 6756

传送门
思路

  • 如果对所有点使用线段树、树状数组,更新的复杂度 d l o g ( d ) dlog(d) dlog(d),d为该点的度数,查询则是 l o g d logd logd,对于 q u e r y = 1 0 5 query=10^5 query=105,更新的复杂度还是太高了
  • 最直接的就是采用分块的思想,将度数大的点建立线段树进行区间更新,小的数直接暴力枚举,此时大度数的更新 n l o g n \sqrt{n}logn n logn,查询 l o g n logn logn,小点的更新 O ( 1 ) O(1) O(1),查询最多 n \sqrt{n} n ,此时的复杂度可以接受
  • 使用线段树写的较慢,用了一大堆优化结果 2808 m s 2808ms 2808ms卡着时间过…
  • 下面是线段树的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
struct node {
    
    
	int l, r, num;
}tree[320][N << 2];
inline void pushUp(int rt, int op) {
    
    
	tree[op][rt].num = tree[op][rt << 1].num + tree[op][rt << 1 | 1].num;
}
int vis[320][N];
void build(int l, int r, int rt, int op) {
    
    
	tree[op][rt].l = l;
	tree[op][rt].r = r;
	if (l == r) {
    
    
		tree[op][rt].num = vis[op][l] ? 1 : 0;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1, op);
	build(mid + 1, r, rt << 1 | 1, op);
	pushUp(rt, op);
}
void update(int ql, int qr, int rt, int op) {
    
    
	if (ql == tree[op][rt].l && tree[op][rt].r == qr) {
    
    
		tree[op][rt].num = vis[op][ql] > 0 ? 1 : 0;
		return;
	}
	int mid = (tree[op][rt].l + tree[op][rt].r) >> 1;
	if (ql <= mid) update(ql, qr, rt << 1, op);
	else if (mid < qr) update(ql, qr, rt << 1 | 1, op);
	pushUp(rt, op);
}
ll query(int rt, int op) {
    
    
	if (tree[op][rt].l == tree[op][rt].r) return tree[op][rt].l;
	if (tree[op][rt << 1].num != (tree[op][rt << 1].r - tree[op][rt << 1].l + 1)) return query(rt << 1, op);
	else return query(rt << 1 | 1, op);
}
int a[N], mark[N], tot, d[N];
vector<int>vec[N], G[N];
bool s[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		int n, m; read(n), read(m);
		for (int i = 1; i <= n; ++i) read(a[i]);
		for (int i = 1; i <= n; ++i) vec[i].clear(), G[i].clear();
		tot = 0;
		for (int i = 1; i <= m; ++i) {
    
    
			int u, v;
			read(u); read(v);
			vec[u].emplace_back(v);
			vec[v].emplace_back(u);
		}
		int block = sqrt(n);
		for (int i = 1; i <= n; ++i) {
    
    
			if ((d[i] = vec[i].size()) >= block) {
    
    
				mark[i] = ++tot;
				for (int j = 0; j < d[i]; ++j) vis[tot][j] = 0;
				for (auto v : vec[i]) {
    
    
					G[v].emplace_back(i);
					if (a[v] < d[i]) ++vis[mark[i]][a[v]];
				}
				build(0, d[i] - 1, 1, mark[i]);
			}
		}
		int Q; read(Q);
		while (Q--) {
    
    
			int op; read(op);
			if (op == 1) {
    
    
				int x, y; read(x), read(y);
				for (auto v : G[x]) {
    
    
					if (a[x] < d[v] && !(--vis[mark[v]][a[x]])) update(a[x], a[x], 1, mark[v]);
					if (y < d[v] && (++vis[mark[v]][y]) == 1) update(y, y, 1, mark[v]);
				}
				a[x] = y;
			}
			else {
    
    
				int x; read(x);
				if (d[x] < block) {
    
    
					for (int i = 0; i < d[x]; ++i) s[i] = 0;
					for (auto v : vec[x]) {
    
    
						if (a[v] >= d[x]) continue;
						s[a[v]] = 1;
					}
					for (int i = 0;; ++i) {
    
    
						if (!s[i]) {
    
    
							printf("%d\n", i);
							break;
						}
					}
				}
				else {
    
    
					printf("%d\n", query(1, mark[x]));
				}
			}
		}
	}
	return 0;
}
  • 使用树状数组快了整整 1 s 1s 1s,惊了 线段树是弟弟
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
#define lowbit(x) ((x)&-(x))
int sum[320][N];
void add(int x, int d, int n, int op) {
    
    
	while (x <= n) {
    
    
		sum[op][x] += d;
		x += lowbit(x);
	}
}
bool check(int x, int op) {
    
    
	int ans = 0;
	int tmp = x;
	while (x > 0) {
    
    
		if (sum[op][x] != lowbit(x)) return 1;
		ans += sum[op][x];
		x -= lowbit(x);
	}
	return ans != tmp;
}
int vis[320][N];
int query(int op, int n) {
    
    
	if (!vis[op][0]) return 0;
	int l = 1, r = n, ans = n;
	while (l <= r) {
    
    
		int mid = l + r >> 1;
		if (check(mid, op)) {
    
    
			r = mid - 1;
			ans = mid;
		}
		else l = mid + 1;
	}
	return ans;
}
int a[N], mark[N], d[N], tot;
vector<int>vec[N], G[N];
bool s[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		int n, m; read(n), read(m);
		for (int i = 1; i <= n; ++i) read(a[i]);
		for (int i = 1; i <= n; ++i) vec[i].clear(), G[i].clear();
		tot = 0;
		while (m--) {
    
    
			int u, v;
			read(u); read(v);
			vec[u].emplace_back(v);
			vec[v].emplace_back(u);
		}
		int block = sqrt(n);
		for (int i = 1; i <= n; ++i) {
    
    
			if ((d[i] = vec[i].size()) >= block) {
    
    
				mark[i] = ++tot;
				for (int j = 0; j <= d[i]; ++j) vis[tot][j] = sum[tot][j] = 0;
				for (auto v : vec[i]) {
    
    
					G[v].emplace_back(i);
					if (a[v] <= d[i]) {
    
    
						if ((++vis[mark[i]][a[v]]) == 1 && a[v]) add(a[v], 1, d[i], mark[i]);
					}
				}
			}
		}
		int Q; read(Q);
		while (Q--) {
    
    
			int op; read(op);
			if (op == 1) {
    
    
				int x, y; read(x), read(y);
				for (auto v : G[x]) {
    
    
					if (a[x] <= d[v] && !(--vis[mark[v]][a[x]]) && a[x]) add(a[x], -1, d[v], mark[v]);
					if (y <= d[v] && (++vis[mark[v]][y]) == 1 && y) add(y, 1, d[v], mark[v]);
				}
				a[x] = y;
			}
			else {
    
    
				int x; read(x);
				if (d[x] < block) {
    
    
					for (int i = 0; i < d[x]; ++i) s[i] = 0;
					for (auto v : vec[x]) {
    
    
						if (a[v] >= d[x]) continue;
						s[a[v]] = 1;
					}
					for (int i = 0;; ++i) {
    
    
						if (!s[i]) {
    
    
							printf("%d\n", i);
							break;
						}
					}
				}
				else {
    
    
					printf("%d\n", query(mark[x], d[x]));
				}
			}
		}
	}
	return 0;
}

Lead of Wisdom

传送门
思路

  • 一开始的时候没往暴力搜索去想,一直在用数学方法写,一直WA,看到别人做得那么快直接怀疑人生
  • 这道题的数据比较小,50个点稍微剪下枝就可以过了,设当前搜过的答案中最大值是 a n s ans ans,先预处理一遍对于每一个 t y p e type type,对应的每个 A , B , C , D A,B,C,D A,B,C,D的最大值都存下来,即使不是同一件,因为当搜到 k k k 层时,若前面的 t y p e type type的和加上包括 k k k 以后的所有的最大值的和,计算出来的结果都比这个 a n s ans ans小的话,那就没必要再搜了,不是同一件的都小了,更别说匹配到一件的 A , B , C , D A,B,C,D A,B,C,D了,这样的剪枝还是有效的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
int a[55][55][4], sz[55];
int maxn[55][4];
int n, k;
ll ans;
ll A, B, C, D;
void dfs(int x) {
    
    
	if (x == 0) {
    
    
		ans = max(ans, (100LL + A) * (100LL + B) * (100LL + C) * (100LL + D));
		return;
	}
	if (ans > (100LL + A + maxn[x][0]) * (100LL + B + maxn[x][1]) * (100LL + C + maxn[x][2]) * (100LL + D + maxn[x][3])) return;
	if (sz[x] == 0) dfs(x - 1);
	for (int i = 1; i <= sz[x]; ++i) {
    
    
		A += a[x][i][0];
		B += a[x][i][1];
		C += a[x][i][2];
		D += a[x][i][3];
		dfs(x - 1);
		A -= a[x][i][0];
		B -= a[x][i][1];
		C -= a[x][i][2];
		D -= a[x][i][3];
	}
}
int main() {
    
    
	int T; read(T);
	for (int i = 0; i < 4; ++i) maxn[0][i] = 0;
	while (T--) {
    
    
		memset(sz, 0, sizeof sz);
		read(n); read(k);
		for (int i = 1; i <= n; ++i) {
    
    
			int t; read(t);
			++sz[t];
			for (int j = 0; j < 4; ++j) {
    
    
				int tmp; read(tmp);
				a[t][sz[t]][j] = tmp;
			}
		}
		for (int i = 1; i <= k; ++i) {
    
    
			for (int j = 0; j < 4; ++j) {
    
    
				int tmp = 0;
				for (int k = 1; k <= sz[i]; ++k) {
    
    
					tmp = max(tmp, a[i][k][j]);
				}
				maxn[i][j] = maxn[i - 1][j] + tmp;
			}
		}
		ans = A = B = C = D = 0;
		dfs(k);
		printf("%lld\n", ans);
	}
	return 0;
}


最后跑出来的时间还挺优秀


The Oculus-HDU 6768

传送门
思路

  • 一开始被这题的数据给劝退…那么大长度的fibo第一反应直接爆整型
  • 后来才意识到,这道考点有点类似hash的考法,用unsigned long long存数会进行溢出取余,将 1 e 6 1e6 1e6长度的fibo数跑了一遍,还真就没有冲撞的
  • 知道这些后就是水题一道
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e6 + 10;
ull fibo[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	fibo[1] = 1, fibo[2] = 2;
	for (int i = 3; i <= 2e6; ++i)	fibo[i] = fibo[i - 1] + fibo[i - 2];
	int T; read(T);
	while (T--) {
    
    
		int t; read(t);
		ull A = 0, B = 0, C = 0;
		for (int i = 1; i <= t; ++i) {
    
    
			int x; read(x);
			if (x) A += fibo[i];
		}
		read(t);
		for (int i = 1; i <= t; ++i) {
    
    
			int x; read(x);
			if (x) B += fibo[i];
		}
		read(t);
		for (int i = 1; i <= t; ++i) {
    
    
			int x; read(x);
			if (x) C += fibo[i];
		}
		C = A * B - C;
		for (int i = 1;; ++i) {
    
    
			if (fibo[i] == C) {
    
    
				printf("%d\n", i);
				break;
			}
		}
	}
	return 0;
}

Total Eclipse-HDU 6763

传送门
思路

  • 首先按照正常的想法去想,先把一个连通块里面权值最小的作为 k k k,进行 k k k次操作,操作完后会分成至少2个的子联通分块,继续进行相同操作直到所有点删光
  • 但上面的操作的 复杂度是 O ( n 2 ) O(n^2) O(n2),不够优秀,先考虑另一种思路,将复杂度降至 O ( n ) , O(n), O(n),这份大佬的博客写得很详细
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
int head[N], cnt;
struct edge {
    
    
	int next, to;
}e[M << 1];
void add(int u, int v) {
    
    
	e[cnt].to = v;
	e[cnt].next = head[u];
	head[u] = cnt++;
}
struct node {
    
    
	int id, x;
}a[N];
int fa[N]; bool vis[N];
int get(int x) {
    
    
	if (x == fa[x]) return x;
	return fa[x] = get(fa[x]);
}
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		int n, m; read(n), read(m);
		memset(head, -1, sizeof head); cnt = 0;
		memset(vis, false, sizeof vis);
		for (int i = 1; i <= n; ++i) {
    
    
			read(a[i].x);
			a[i].id = i;
		}
		for (int i = 1; i <= m; ++i) {
    
    
			int u, v; read(u), read(v);
			add(u, v);
			add(v, u);
		}
		sort(a + 1, a + 1 + n, [](const node& a, const node& b) {
    
    
			return a.x > b.x;
			});
		for (int i = 1; i <= n; ++i) {
    
    
			fa[i] = i;
		}
		ll tot = 0, ans = 0; a[n + 1].x = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			++tot;
			int u = a[i].id;
			vis[u] = 1;
			int fx = get(u);
			for (int i = head[u]; ~i; i = e[i].next) {
    
    
				int v = e[i].to;
				if (vis[v]) {
    
    
					int fy = get(v);
					if (fx != fy) {
    
    
						fa[fy] = fx;
						--tot;
					}
				}
			}
			ans += tot * (a[i].x - a[i + 1].x);
		}
		printf("%lld\n", ans);
	}
	return 0;
}

Tokitsukaze and Multiple-HDU 6794

传送门
思路

  • 先将n以内所有的前缀和 s u m [ i ] % p sum[i] \%p sum[i]%p求出来,后面利用前缀和的思想,若 s u m [ i ] sum[i] sum[i] i i i以前出现过一次,则两者的差值必定是 p p p的倍数
  • 另外如果上一个 s u m [ i ] sum[i] sum[i] i i i之间以经存在一个 p p p的倍数,这个 i i i就不算在内
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
int a[N], sum[N];
int vis[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		int n, p; read(n), read(p);
		sum[0] = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			read(a[i]);
			a[i] %= p;
			sum[i] = (sum[i - 1] + a[i]) % p;
		}
		for (int i = 1; i <= p; ++i) vis[i] = -1;
		int ans = 0, pos = 0;
		vis[0] = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			if (vis[sum[i]] >= pos) {
    
    
				++ans;
				pos = i;
			}
			vis[sum[i]] = i;
		}
		printf("%d\n", ans);
	}
	return 0;
}


Little W and Contest -HDU 6795

传送门
思路

  • c o d e r 总 数 t o t c , r e a d e r 总 数 t o t r coder总数totc,reader总数totr codertotc,readertotr
  • 首先求出一开始的 a n s = C t o t r 1 C t o t c 2 + C t o t c 3 ans=C^1_{totr}C^2_{totc}+C^3_{totc} ans=Ctotr1Ctotc2+Ctotc3
  • 首先根据每个点是 r e a d e r   o r   c o d e r reader\ or\ coder reader or coder,确定初始 n u m c [ i ] , n u m r [ i ] numc[i],numr[i] numc[i],numr[i]
  • 对于 n − 1 n-1 n1条边的处理,利用并查集的思想,共分为4种情况
    1. C r ( x ) 1 ∗ C c ( y ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{r(x)}^1*C_{c(y)}^1*C_{totc-c(x)-c(y)}^1 Cr(x)1Cc(y)1Ctotcc(x)c(y)1
    2. C r ( y ) 1 ∗ C c ( x ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{r(y)}^1*C_{c(x)}^1*C_{totc-c(x)-c(y)}^1 Cr(y)1Cc(x)1Ctotcc(x)c(y)1
    3. C c ( x ) 1 ∗ C c ( y ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{c(x)}^1*C_{c(y)}^1*C_{totc-c(x)-c(y)}^1 Cc(x)1Cc(y)1Ctotcc(x)c(y)1
    4. C t o t r − r ( x ) − r ( y ) 1 ∗ C c ( x ) 1 ∗ C c ( y ) 1 C_{totr-r(x)-r(y)}^1*C_{c(x)}^1*C_{c(y)}^1 Ctotrr(x)r(y)1Cc(x)1Cc(y)1
  • 处理完后将 x , y x,y x,y合并,并累加对应的 c , r c,r c,r,详情看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
inline ll C(ll n, int k) {
    
    
	if (n < k) return 0;
	if (k == 1) {
    
    
		return n;
	}
	if (k == 2) {
    
    
		return n * (n - 1) / 2 % mod;
	}
	return n * (n - 1) * (n - 2) / 2 / 3 % mod;
}
int fa[N];
int get(int x) {
    
    
	if (x == fa[x]) return x;
	return fa[x] = get(fa[x]);
}
int totc, totr, a[N], numc[N], numr[N];
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		int n; read(n);
		totc = totr = 0;
		for (int i = 1; i <= n; ++i) fa[i] = i;
		for (int i = 1; i <= n; ++i) {
    
    
			read(a[i]);
			if (a[i] & 1) ++totr, numr[i] = 1, numc[i] = 0;
			else ++totc, numc[i] = 1, numr[i] = 0;
		}
		ll ans = (C(totr, 1) * C(totc, 2) % mod + C(totc, 3)) % mod;
		printf("%lld\n", ans);
		for (int i = 1; i < n; ++i) {
    
    
			int x, y; read(x), read(y);
			int fx = get(x), fy = get(y);
			if (fx != fy) {
    
    
				ans = (ans - C(numr[fx], 1) * C(numc[fy], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
				ans = (ans - C(numr[fy], 1) * C(numc[fx], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
				ans = (ans - C(numc[fx], 1) * C(numc[fy], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
				ans = (ans - C(numc[fx], 1) * C(numc[fy], 1) % mod * C(totr - numr[fx] - numr[fy], 1) % mod + mod) % mod;
				numc[fx] += numc[fy];
				numr[fx] += numr[fy];
				fa[fy] = fx;
			}
			printf("%lld\n", ans);
		}
	}
	return 0;
}

其实这道题最大的数据也不会爆 l o n g   l o n g long\ long long long,所以可以在输出的时候再进行mod操作也是可行的


Parentheses Matching-HDU 6799

传送门
思路

  • 对于这种经典的括号匹配,首先容易想到用单调栈来匹配左括号与右括号,这题还有额外的条件,按照字典序输出括号串,所以用栈来存左括号,使用双向队列来存 * ,需要左括号进行匹配就从前面提取,需要匹配右括号就从后面提取,保证答案为最小字典序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 1e5 + 10;
char s[N];
int len;
bool check() {
    
    
	len = strlen(s);
	deque<int>que;
	stack<int>stk;
	for (int i = 0; i < len; ++i) {
    
    
		if (s[i] == '*') {
    
    
			que.push_back(i);
		}
		else if (s[i] == '(') stk.push(i);
		else if (s[i] == ')') {
    
    
			if (!stk.empty()) {
    
    
				stk.pop();
			}
			else if (!que.empty()) {
    
    
				s[que.front()] = '(';
				que.pop_front();
			}
			else return false;
		}
	}
	if (stk.size() > que.size()) return false;
	while (!stk.empty() && !que.empty()) {
    
    
		if (que.back() < stk.top()) return false;
		s[que.back()] = ')';
		que.pop_back();
		stk.pop();
	}
	return stk.empty();
}
int main() {
    
    
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	int T; read(T);
	while (T--) {
    
    
		scanf("%s", s);
		if (check()) {
    
    
			for (int i = 0; i < len; ++i) {
    
    
				if (s[i] == '(' || s[i] == ')') printf("%c", s[i]);
			}
			puts("");
		}
		else puts("No solution!");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bloom_er/article/details/108051090
今日推荐