对于每种字符,其要么不选,要么选择一个,所以答案是 ∏ ( c n t i − 1 ) − 1 \prod (cnt_i-1)-1 ∏(cnti−1)−1 。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
string s;
cin >> n >> s;
vector<int> cnt(26);
for (auto c : s) {
++cnt[c - 'a'];
}
int ans = 1;
for (auto x : cnt) {
ans = mul(ans, x + 1);
}
cout << ans - 1 << "\n";
return 0;
}
记 d p i dp_i dpi 表示考虑前 i i i 个的答案,如果 a i = a i − 1 a_i = a_{i-1} ai=ai−1 则对 i i i 操作和 i − 1 i-1 i−1 操作等价;否则可以从 a i = a j + 1 , a i ≠ a j a_i = a_{j+1}, a_i\neq a_j ai=aj+1,ai̸=aj 的位置转移过来,对于每种不同的 a a a 记录 d p dp dp 值之和即可。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
int m = *max_element(a.begin(), a.end()) + 1;
vector<int> sum(m);
vector<int> dp(n + 1);
dp[0] = 1;
for (int i = 0; i < n; ++i) {
dp[i + 1] = dp[i];
if (!i || a[i] != a[i - 1]) {
add(dp[i + 1], sum[a[i]]);
}
if (!i || a[i] != a[i - 1]) {
add(sum[a[i]], dp[i]);
}
}
cout << dp[n] << "\n";
return 0;
}
将所有数都异或上 a a a ,则原问题等价于起点是 0 0 0 。由于位的顺序不影响答案,所以可以认为终点是 2 k − 1 2^k-1 2k−1 。当 k k k 是偶数的时候,考虑奇偶性不难发现无解。否则考虑对于 k k k 是奇数构造 0 → 2 k − 1 0\to 2^k-1 0→2k−1 ,以及对于任意 k k k 构造 0 → 1 0\to 1 0→1 。第二部分很简单,用 k − 1 k-1 k−1 来归纳构造即可;第一部分则考虑用 k − 2 k-2 k−2 归纳构造:
00000000
........(apply 0->2^x-1 for x=k-2)
11111100
11111110
........(apply 0->1 for x=k-2)
01111110
01111111
........(apply 0->1 for x=k-1)
11111111
之后,让后 k k k 位从 0 0 0 走到 2 k − 1 2^k-1 2k−1 ,每走一步前 n − k n-k n−k 位就走一次 0 → 1 0\to 1 0→1 的构造即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, a, b;
cin >> n >> a >> b;
int k = __builtin_popcount(a ^ b);
if (k % 2 == 0) {
cout << "NO" << "\n";
return 0;
}
cout << "YES" << "\n";
vector<vector<int>> to_one(n + 1);
to_one[0] = {0};
to_one[1] = {0, 1};
for (int i = 2; i <= n; ++i) {
for (auto x : to_one[i - 1]) {
to_one[i].push_back(x << 1);
}
reverse(to_one[i - 1].begin(), to_one[i - 1].end());
for (auto x : to_one[i - 1]) {
to_one[i].push_back(x << 1 | 1);
}
reverse(to_one[i - 1].begin(), to_one[i - 1].end());
}
vector<vector<int>> to_all(k + 1);
to_all[1] = {0, 1};
for (int i = 3; i <= k; i += 2) {
for (auto x : to_all[i - 2]) {
to_all[i].push_back(x);
}
for (auto x : to_one[i - 2]) {
to_all[i].push_back(x ^ ((1 << (i - 1)) - 1));
}
for (auto x : to_one[i - 1]) {
to_all[i].push_back(x ^ ((1 << i) - 2));
}
}
vector<int> ans;
for (auto x : to_all[k]) {
for (auto y : to_one[n - k]) {
ans.push_back(y << k | x);
}
reverse(to_one[n - k].begin(), to_one[n - k].end());
}
vector<int> to_bit(n);
int ptr = 0;
for (int i = 0; i < n; ++i) {
if ((a ^ b) >> i & 1) {
to_bit[ptr++] = i;
}
}
for (int i = 0; i < n; ++i) {
if (!((a ^ b) >> i & 1)) {
to_bit[ptr++] = i;
}
}
for (int i = 0; i < 1 << n; ++i) {
int real_ans = 0;
for (int j = 0; j < n; ++j) {
if (ans[i] >> j & 1) {
real_ans |= 1 << to_bit[j];
}
}
ans[i] = real_ans;
}
for (int i = 0; i < 1 << n; ++i) {
if (i) {
cout << " ";
}
cout << (ans[i] ^ a);
}
cout << "\n";
return 0;
}
不难发现 f ( p , q ) = q × p − 1 f(p, q) = q\times p^{-1} f(p,q)=q×p−1 ,找规律 归纳可得:
$a_m = (q{-1}pqp{-1})^{\lfloor \frac{(m-2)}{6}\rfloor} \times f[(m-2)\bmod6] \times (pq{-1}p{-1}q)^{\lfloor \frac{(m-2)}{6}\rfloor} $
其中 f [ i ] f[i] f[i] 是一个和 p , q p, q p,q 有关的式子,是 O ( 1 ) O(1) O(1) 的。
#include
using namespace std;
vector<int>& operator *= (vector<int> &p, vector<int> q) {
vector<int> r = p;
for (int i = 0; i < (int) p.size(); ++i) {
p[i] = q[r[i]];
}
return p;
}
vector<int> operator * (vector<int> p, vector<int> q) {
return p *= q;
}
vector<int> inv(const vector<int> &p) {
int n = (int) p.size();
vector<int> res(n);
for (int i = 0; i < n; ++i) {
res[p[i]] = i;
}
return res;
}
vector<int> power(vector<int> x, int y) {
int n = (int) x.size();
vector<int> res(n);
for (int i = 0; i < n; ++i) {
res[i] = i;
}
while (y) {
if (y & 1) {
res *= x;
}
x *= x;
y >>= 1;
}
return res;
}
void output(vector<int> ans) {
for (int i = 0; i < (int) ans.size(); ++i) {
if (i) {
cout << " ";
}
cout << ans[i] + 1;
}
cout << "\n";
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
--p[i];
}
vector<int> q(n);
for (int i = 0; i < n; ++i) {
cin >> q[i];
--q[i];
}
if (m == 1) {
output(p);
} else if (m == 2) {
output(q);
} else {
m -= 2;
vector<int> inv_p = inv(p);
vector<int> inv_q = inv(q);
vector<int> ans(n);
for (int i = 0; i < n; ++i) {
ans[i] = i;
}
ans *= power(inv_q * p * q * inv_p, m / 6);
switch (m % 6) {
case 0:
ans *= q;
break;
case 1:
ans *= inv_p * q;
break;
case 2:
ans *= inv_q * inv_p * q;
break;
case 3:
ans *= inv_q * p * inv_q * inv_p * q;
break;
case 4:
ans *= inv_q * p * p * inv_q * inv_p * q;
break;
case 5:
ans *= inv_q * p * q * p * inv_q * inv_p * q;
break;
}
ans *= power(p * inv_q * inv_p * q, m / 6);
output(ans);
}
return 0;
}
枚举最后选了多少个,这样限制就变成了某个前缀最多或最少选若干个,搞个上下界费用流就行了。
#include
using namespace std;
using cap_t = int;
using cost_t = long long;
const cap_t cap_inf = 0x3f3f3f3f;
const cost_t cost_inf = 1ll << 60;
namespace flow {
struct edge {
int to, rev;
cost_t cost;
cap_t cap;
edge(int t, cap_t c, cost_t w, int r) {
to = t;
rev = r;
cap = c;
cost = w;
}
};
vector<vector<edge>> adj;
vector<cost_t> dist;
int n, source, sink;
vector<bool> visit;
vector<int> cur;
cost_t res;
cap_t ans;
void init(int v, int s, int t) {
n = v;
ans = res = 0;
source = s;
sink = t;
adj.clear();
adj.resize(n);
cur.resize(n);
dist.resize(n);
visit.resize(n);
}
void add(int x, int y, cap_t c, cost_t w) {
adj[x].emplace_back(y, c, w, adj[y].size());
adj[y].emplace_back(x, 0, -w, adj[x].size() - 1);
}
bool spfa() {
queue<int> q;
for (int i = 0; i < n; ++i) {
dist[i] = cost_inf;
visit[i] = false;
cur[i] = 0;
}
dist[source] = 0;
q.push(source);
while (!q.empty()) {
int x = q.front();
q.pop();
visit[x] = false;
for (auto e : adj[x]) {
if (e.cap && dist[e.to] > dist[x] + e.cost) {
dist[e.to] = dist[x] + e.cost;
if (!visit[e.to]) {
visit[e.to] = true;
q.push(e.to);
}
}
}
}
return dist[sink] != cost_inf;
}
cap_t dfs(int x, cap_t f) {
if (x == sink) {
return f;
}
visit[x] = true;
cap_t res = 0;
for (int &i = cur[x]; i < (int) adj[x].size(); ++i) {
edge &e = adj[x][i];
if (e.cap && dist[e.to] == dist[x] + e.cost && !visit[e.to]) {
cap_t w = dfs(e.to, min(e.cap, f - res));
res += w;
e.cap -= w;
adj[e.to][e.rev].cap += w;
if (res == f) {
visit[x] = false;
return res;
}
}
}
dist[x] = cost_inf;
return res;
}
pair<cap_t, cost_t> min_cost_max_flow() {
while (spfa()) {
cap_t flow = dfs(source, cap_inf);
ans += flow;
res += flow * dist[sink];
}
return make_pair(ans, res);
}
}
using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::min_cost_max_flow;
const int MAX = 100;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> x(n), y(n);
vector<long long> z(n);
for (int i = 0; i < n; ++i) {
cin >> x[i] >> y[i] >> z[i];
}
vector<int> l(MAX, n), r(MAX, n), d(MAX, n), u(MAX, n);
int m;
cin >> m;
while (m--) {
string type;
int a, b;
cin >> type >> a >> b;
--a;
if (type == "L") {
l[a] = b;
} else if (type == "R") {
r[a] = b;
} else if (type == "D") {
d[a] = b;
} else {
u[a] = b;
}
}
for (int i = MAX - 2; ~i; --i) {
l[i] = min(l[i], l[i + 1]);
d[i] = min(d[i], d[i + 1]);
}
for (int i = 1; i < MAX; ++i) {
r[i] = min(r[i], r[i - 1]);
u[i] = min(u[i], u[i - 1]);
}
long long ans = 0;
auto solve = [&](int take) {
init(MAX * 2 + 4, MAX * 2 + 2, MAX * 2 + 3);
add(source, 0, take, 0);
add(MAX + 1, sink, take, 0);
int need = take;
for (int i = 0; i < MAX; ++i) {
int low = max(0, take - l[max(i - 1, 0)]);
int high = r[i];
if (low > high) {
return;
}
add(i, i + 1, high - low, 0);
add(source, i + 1, low, 0);
add(i, sink, low, 0);
need += low;
}
for (int i = 0; i < MAX; ++i) {
int low = max(0, take - d[max(i - 1, 0)]);
int high = u[i];
if (low > high) {
return;
}
add(MAX + i + 2, MAX + i + 1, high - low, 0);
add(source, MAX + i + 1, low, 0);
add(MAX + i + 2, sink, low, 0);
need += low;
}
for (int i = 0; i < n; ++i) {
add(x[i], MAX + y[i] + 1, 1, -z[i]);
}
pair<int, long long> res = min_cost_max_flow();
if (res.first == need) {
ans = max(ans, -res.second);
}
};
for (int i = 1; i <= n; ++i) {
solve(i);
}
cout << ans << "\n";
return 0;
}
倒过来考虑这个问题,走一条边 c c c 就是 x → 2 x + c x\to 2x+c x→2x+c ,因为模数是奇数,所以 2 2 2 有逆元,即这是一个双射,所以一定能回到自己。
注意到一个点走两次一条边会回到自己, x → 4 x + 3 c x\to 4x+3c x→4x+3c ,那么如果有另一条边 c ′ c' c′ ,则我们能凑出 3 ( c − c ′ ) 3(c-c') 3(c−c′) 。记 g g g 是所有边权之差以及模数的 gcd \gcd gcd ,那么我们只需要在模 gcd ( 3 g , M O D ) \gcd(3g, MOD) gcd(3g,MOD) 意义下考虑这个问题即可(此时模数要么是 g g g 要么是 3 g 3g 3g )。
假设所有边模 g g g 都为 z z z ,那么令 x ′ = x + z x'=x+z x′=x+z ,将边权减去 z z z ,走一步后新的 x ′ x' x′ 会变成 2 ( x ′ − z ) + ( c + z ) + z = 2 x ′ + c 2(x'-z) + (c+z) + z = 2x' + c 2(x′−z)+(c+z)+z=2x′+c ,和原来的规则一样,所以我们可以认为在新的图中,每条边都是 g g g 的倍数。
对于 ( 1 , x ) (1,x) (1,x) ,其能到的状态可以写成 ( v , 2 a x + p g ) ( 0 ≤ p < 3 ) (v, 2^ax+pg)(0\le p<3) (v,2ax+pg)(0≤p<3) 的形式,由之前的变换 x → 4 x + 3 c x\to 4x+3c x→4x+3c ,且 c c c 是 g g g 的倍数,得 x x x 和 4 x 4x 4x 是等价状态,故 0 ≤ a < 2 0\le a<2 0≤a<2 。这样新的图中点数就只有 O ( n ) O(n) O(n) 个了,用并查集连一下就好了。
#include
using namespace std;
class dsu {
public:
vector<int> p;
int n;
dsu(int n): n(n) {
p.resize(n);
for (int i = 0; i < n; ++i) {
p[i] = i;
}
}
inline int find(int x) {
while (x != p[x]) {
x = p[x] = p[p[x]];
}
return x;
}
inline bool unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
} else {
p[x] = y;
return true;
}
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, q, md;
cin >> n >> m >> q >> md;
int g = md;
vector<int> from(m), to(m), cost(m);
for (int i = 0; i < m; ++i) {
cin >> from[i] >> to[i] >> cost[i];
--from[i];
--to[i];
g = __gcd(g, abs(cost[i] - cost[0]));
}
if ((md / g) % 3 == 0) {
md = g * 3;
} else {
md = g;
}
int z = cost[0] % g;
dsu p(n * 6);
for (int i = 0; i < m; ++i) {
int w = (cost[i] - z) / g % 3;
for (int j = 0; j < 3; ++j) {
p.unite(from[i] * 6 + j * 2 + 0, to[i] * 6 + (2 * j + w) % 3 * 2 + 1);
p.unite(from[i] * 6 + j * 2 + 1, to[i] * 6 + (2 * j + w) % 3 * 2 + 0);
p.unite(to[i] * 6 + j * 2 + 0, from[i] * 6 + (2 * j + w) % 3 * 2 + 1);
p.unite(to[i] * 6 + j * 2 + 1, from[i] * 6 + (2 * j + w) % 3 * 2 + 0);
}
}
vector<vector<bool>> can(2, vector<bool>(md));
for (int i = 0, j = z; i < md * 2; ++i, j = j * 2 % md) {
can[i & 1][j] = true;
}
while (q--) {
int s, t, r;
cin >> s >> t >> r;
--s;
--t;
bool ans = false;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 2; ++j) {
if (p.find(t * 6) == p.find(s * 6 + i * 2 + j)) {
if (can[j][(r + z + (3 - i) * g) % md]) {
ans = true;
}
}
}
}
cout << (ans ? "YES" : "NO") << "\n";
}
return 0;
}