A、串
动态规划。处理即可,较为基础,也可容斥。
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) #define all(__vv__) (__vv__).begin(), (__vv__).end() #define endl "\n" #define pai pair<int, int> #define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__)) #define rep(i, sta, en) for(int i=sta; i<=en; ++i) #define repp(i, sta, en) for(int i=sta; i>=en; --i) typedef long long ll; typedef unsigned long long ull; typedef long double ld; inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; } inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); } inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; } ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; } const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} }; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; const int N = 1e6 + 7; ll n, m; ll dp[N][3]; void solve() { dp[1][0] = 25; // 前i个不包含u字母的 dp[1][1] = 1; // 前i个包含u不包含s的 dp[1][2] = 0; // 前i个包含us的 n = read(); rep(i, 2, n) { dp[i][0] = dp[i - 1][0] * 25 % MOD; dp[i][1] = (dp[i - 1][0] + dp[i - 1][1] * 25) % MOD; dp[i][2] = (dp[i - 1][1] + dp[i - 1][2] * 26) % MOD; m = (m + dp[i][2]) % MOD; } print(m); } int main() { //int T = read(); while (T--) solve(); return 0; }
B、括号
很容易发现每一个左括号的贡献就是它后面有几个右括号,一次把左括号贡献累加就是总答案。现在唯一的限制条件就是我们构造的括号序列。我们还知道如果是对称也就是例如这样的括号序列,它的答案贡献就是左括号个数乘以右括号个数。那么我们最终要构造出来的也就是量级,那么直接放下个右括号先,再判断需要在最左边加多少个右括号,我们就可以得到一个倍数的括号数,那么再把余数补齐即可成功构造。
void solve() { n = read(); m = n / 50000; // 5e4*5e4>1e9一定可以构造1e9 rep(i, 1, m) printf("("); int p = n % 50000; repp(i, 50000, 1) { if (i == p) printf("("); // 补齐余数 printf(")"); } }
C、红和蓝
红色要和一个红色连边并且只能和一个红色连边,蓝色一定要和另外一个蓝色连边并且只能和一个蓝色连边,但是红色和蓝色之间的边没有限制。那么转换一下题目意思。就是问有没有一种圈数方法,每次圈2个数,使得树中每个节点只被使用一次并且最后保证全部数都被圈过。
我们最后要使得全部数都被圈过一次,那么能和叶子节点圈一起的也就只有它父亲了,也就是变相的说如果一个叶子节点的父亲除了他还有别的孩子那么这棵树就死了。但是我们在实现的时候可以从下往上回溯编号,使得叶子节点和它父亲编号一致,再使得叶子节点的父亲的父亲和它父亲编号一致,这样一直处理,看看会不会存在冲突的点,也就是一个点本来要和儿子一个编号,但是又被别人占有了,说明不行。还有根节点比较特殊它没有父亲,如果要根节点和它父亲编号的话也说明这个树无解。
const int N = 1e5 + 7; ll n, m; int f[N], cnt; vector<int> edge[N]; bool flag; void dfs(int u, int fa) { bool son = 0; for (auto& v : edge[u]) { if (v == fa) continue; son = 1; dfs(v, u); } if (son == 0 or !f[u]) { // 从下往上标对数,如果冲突,那就无解 if (f[fa] or fa == 0) { flag = 1; } else f[u] = f[fa] = ++cnt; } } int color[N]; void dfs(int u, int fa, int col) { color[u] = col; for (auto& v : edge[u]) { if (v == fa) continue; if (f[u] == f[v]) dfs(v, u, col); else dfs(v, u, col ^ 1); } } void solve() { n = read(); if (n == 1) { print(-1); return; } rep(i, 2, n) { int u = read(), v = read(); edge[u].push_back(v); edge[v].push_back(u); } dfs(1, 0); // 标对数 if (flag) { print(-1); return; } dfs(1, 0, 1); // 找答案 rep(i, 1, n) if (color[i]) printf("R"); else printf("B"); }
D、点一成零
看到整个题目最关键的几个字翻转之后可继承到下一次查询。第一步如果我们不实现翻转操作,那么我们对起始地图的方案数是多少如何计算,这个会等于连通块数的阶乘,乘以个个连通块的大小。准确的说是阶乘实现连通块顺序,实现连通块中挑选一个数按下去,连通块大小使用并查集维护,并且题目所说改变保留到下一次,才可与使用并查集维护,并查集方便维护合并集合,但是拆集合的时候就很难实现了。
那么现在多了翻转操作之后会怎么样,如果按上了之前就是的点,说明答案不变。如果按到了的点,那么它就可能会多一个连通块,也可能连通块数不变,也可能连通块数量减。
const int N = 500 + 7; ll n, m; int fa[N * N], sz[N * N]; char mp[N][N]; int find(int