[题解]Wordle不是一次就能解决问题吗?
\(\mathcal{Back\;To\;The\;Menu}\).
2022-03-16 Wordle不是一次就能解决问题吗?
怎么说呢,感觉自己还是代码能力太弱了......T1 一个朴素的分块都可以写这么久,着实不应该啊。
传教 / Mission
先定义 \(c_i=a_i\oplus b_i\),最后的目标就是把 \(c_i\) 全部变成 \(0\).
由于是区间操作,所以你可以做一个差分:\(s_i=c_i\oplus c_{i-1}\),并且目标不变 —— 将 \(s_i\) 全部变为 \(0\).
对于一个区间整体异或一个数,实际上就是把差分数组的 \(s_i,s_{i+k}\) 同时异或一个数,你会发现,经过这个操作后,下标余数相同的数的异或和不变,然后你就会判断无解了 —— 某个下标与 \(k\) 余数相同的 \(s_i\) 异或和不为 \(0\).
判断无解的条件给了你思路 —— 将所有数按照膜 \(k\) 剩余系分组。对于同一剩余系中的数,按照下标排列好,不需要执行的异或传递就是前缀异或和为 \(0\) 的位置个数,也就是说,我们要动态维护每个剩余系下前缀异或和为 \(0\) 的位置个数。
线段树等 \(\mathcal O(\text{polylog})\) 的算法显然是不行的,考虑分块维护,因为 \(s_i\le 2^{15}\)。显然可以每个块开一个桶维护一下。
但是你发现 \(k\) 比较大的时候,剩余系本来就比较小,再开桶就显得小题大做 MLE,因此可以对于 \(k\) 进行根号分治:
- \(k>\sqrt n\) 的时候,直接暴力维护剩余系前缀异或和;
- \(k\le \sqrt n\) 时,每个剩余系分块维护;
然后就做完了,时间复杂度 \(\mathcal O(n\sqrt n)\). 不知道为什么下面这份代码只有 \(85pts\),听说有些数据有点问题(但是好像只有一组),但是我也懒得管了......
/** @author __Elaina__ */
// #pragma GCC optimize("O2")
#include <bits/stdc++.h>
using namespace std;
// #define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
template<class T> inline T listMax(const T& x) { return x; }
template<class T, class... Args> inline T listMax(const T& x, const Args&... args) {
return max(x, listMax(args...));
}
template<class T> inline T listMin(const T& x) { return x; }
template<class T, class... Args> inline T listMin(const T& x, const Args&... args) {
return min(x, listMin(args...));
}
} // namespace Elaina
using namespace Elaina;
const int Maxn = 2e5;
const int Sqn = 450;
const int Maxa = 1 << 15;
const int Maxk = 2e5;
int n, k, q;
int a[Maxn + 5], b[Maxn + 5], s[Maxn + 5];
inline void input() {
readin(n, k, q);
repf (i, 0, n) readin(a[i]);
repf (i, 0, n) readin(b[i]);
repf (i, 0, n) s[i] = a[i] ^ b[i];
drep (i, n - 1, 0) s[i] ^= s[i - 1];
// repf (i, 0, n) masdf("s[%d] == %d\n", i, s[i]);
}
int cnt0 = 0, allzero = 0;
struct residueDividePart {
bool F__K_SPECIAL;
int len, id, xorsum; // the length of this residue & the total id of blocks & the xor sum
vector<int> pre; // the presum of the origin array
vector<int> bel; // the belong of each position
// below are block info
vector<vset> cnt; // counter for each block
vector<int> blef, brig; // the left & right endpos of each block
vector<int> tag; // lazy tag
inline void init(const vset& arr) {
// masdf("init :> "); for (const int& x: arr) masdf("%d ", x); Endl;
len = arr.size();
pre.resize(len), bel.resize(len);
xorsum = pre[0] = arr[0];
cnt0 += (pre[0] == 0);
repf (i, 1, len) {
pre[i] = pre[i - 1] ^ arr[i];
cnt0 += (pre[i] == 0);
xorsum = pre[i];
// masdf("When i == %d, pre[%d] == %d, xorsum == %d\n", i, i, pre[i], xorsum);
}
for (id = 1; id * Sqn < len; ++id);
blef.resize(id), brig.resize(id), tag.resize(id), cnt.resize(id);
repf (i, 0, id) {
blef[i] = i * Sqn, brig[i] = min((i + 1) * Sqn, len);
cnt[i].resize(Maxa);
repf (j, blef[i], brig[i]) ++cnt[i][pre[j]];
}
if (!F__K_SPECIAL) allzero += (xorsum != 0);
}
inline void rebuild(int i) {
if (!tag[i]) return ;
cnt0 -= cnt[i][tag[i]];
repf (j, blef[i], brig[i]) {
--cnt[i][pre[j]];
++cnt[i][pre[j] ^= tag[i]];
}
cnt0 += cnt[i][tag[i] = 0];
}
inline void modify(int p, int x) {
if (!x) return ;
assert(p < len);
if (!F__K_SPECIAL) allzero -= (xorsum != 0);
xorsum ^= x;
if (!F__K_SPECIAL) allzero += (xorsum != 0);
rebuild(bel[p]);
repf (i, p, brig[bel[p]]) {
cnt0 -= (pre[i] == 0);
--cnt[bel[p]][pre[i]];
pre[i] ^= x;
cnt0 += (pre[i] == 0);
++cnt[bel[p]][pre[i]];
}
repf (i, bel[p] + 1, id) {
cnt0 -= cnt[i][tag[i]];
tag[i] ^= x;
cnt0 += cnt[i][tag[i]];
}
// masdf("xorsum == %d, cnt0 == %d\n", xorsum, cnt0);
}
} Arrs[Maxk + 5];
int pos[Maxn + 5]; // the position of the element in its residue group
inline void prelude() {
vector<int> arr;
repf (i, 0, k) {
arr.clear(); int p = 0;
for (int j = i; j < n; j += k) {
arr.push_back(s[j]);
pos[j] = p++;
}
if (n % k == i) Arrs[i].F__K_SPECIAL = true;
else Arrs[i].F__K_SPECIAL = false;
Arrs[i].init(arr);
}
}
inline void solve() {
char op[5]; int x, y;
if (allzero) puts("-1");
else printf("%d\n", n - cnt0);
while (q--) {
scanf("%s %d %d", op, &x, &y), --x;
if (*op == 'a') {
int delt = a[x] ^ y;
a[x] = y;
Arrs[x % k].modify(pos[x], delt);
if (x + 1 < n) Arrs[(x + 1) % k].modify(pos[x + 1], delt);
}
else {
int delt = b[x] ^ y;
b[x] = y;
Arrs[x % k].modify(pos[x], delt);
if (x + 1 < n) Arrs[(x + 1) % k].modify(pos[x + 1], delt);
}
if (allzero) puts("-1");
else printf("%d\n", n - cnt0);
}
}
namespace Brute {
int cnt0 = 0, allzero = 0;
struct residueDividePart {
bool F__K_SPECIAL;
int len, xorsum;
vector<int> pre;
inline void init(const vset& arr) {
pre.resize(len = arr.size());
xorsum = pre[0] = arr[0];
cnt0 += (pre[0] == 0);
repf (i, 1, len) {
pre[i] = pre[i - 1] ^ arr[i];
cnt0 += (pre[i] == 0);
xorsum = pre[i];
}
if (!F__K_SPECIAL) allzero += (xorsum != 0);
}
inline void modify(int p, int x) {
if (!x) return ;
assert(p < len);
if (!F__K_SPECIAL) allzero -= (xorsum != 0);
xorsum ^= x;
if (!F__K_SPECIAL) allzero += (xorsum != 0);
repf (i, p, len) {
cnt0 -= (pre[i] == 0);
pre[i] ^= x;
cnt0 += (pre[i] == 0);
}
// masdf("xorsum == %d, cnt0 == %d\n", xorsum, cnt0);
}
} Arrs[Maxk + 5];
int pos[Maxn + 5]; // the position of the element in its residue group
inline void prelude() {
vector<int> arr;
repf (i, 0, k) {
arr.clear(); int p = 0;
for (int j = i; j < n; j += k) {
arr.push_back(s[j]);
pos[j] = p++;
}
if (n % k == i) Arrs[i].F__K_SPECIAL = true;
else Arrs[i].F__K_SPECIAL = false;
Arrs[i].init(arr);
}
}
inline void solve() {
char op[5]; int x, y;
if (allzero) puts("-1");
else printf("%d\n", n - cnt0);
while (q--) {
scanf("%s %d %d", op, &x, &y), --x;
if (*op == 'a') {
int delt = a[x] ^ y;
a[x] = y;
Arrs[x % k].modify(pos[x], delt);
if (x + 1 < n) Arrs[(x + 1) % k].modify(pos[x + 1], delt);
}
else {
int delt = b[x] ^ y;
b[x] = y;
Arrs[x % k].modify(pos[x], delt);
if (x + 1 < n) Arrs[(x + 1) % k].modify(pos[x + 1], delt);
}
if (allzero) puts("-1");
else printf("%d\n", n - cnt0);
}
}
inline void work() {
prelude();
solve();
}
} // namespace Brute
signed main() {
freopen("mission.in", "r", stdin);
freopen("mission.out", "w", stdout);
input();
if (k <= Sqn) {
prelude();
solve();
}
else Brute::work();
return 0;
}
方程 / Equation
显然是要我们对这个式子动手脚的,它说要动就动呗,毕竟它也只给了我们这个式子啊......注意看原来的式子,你会发现稍微变形之后 \(a+b\) 都是一起出现的,这提示我们可以换元:
其实有 \((2)\) 你就可以进行 \(\mathcal O(n^2)\) 的算法,常数小一点有 \(60pts\)(但是我看在场好像还没有超级无敌小常数的诶~~)把 \(a+b\) 换元之后可以得到 \((3)\). 有了 \((3)\),你可以很容易发现:若 \((x,c)\) 满足条件,那么 \((kx,kc)\) 也满足条件,因为所有项都是二次项,同时乘以一个系数也不影响。
于是我们可以只需要统计 \(c=1\) 的情况,其他情况直接用 \(k\) 进行调整。
接下来有几个问题:一是 \(a+b\equiv i\pmod p\) 有多少组,这个直接上 NTT 就行了。
其二,我们最终要计算的是 \(kx(k\in S)\),即算出一个 \((x,1)\) 之后,我们实际上要找到 \((kx,k)(k\in S)\) 的方案数,即对于一个 \(x\),有多少 \(x,k\) 的方案数达到 \(kx\),可以考虑使用原根 \(g\),将 \(kx\) 表示为 \(g^u\times g^v\),其中 \(k=g^u,x=g^v\),这就又变成一个背包问题了,使用 NTT 同样可以解决。然后就解决了,总复杂度 \(\mathcal O(n\log n)\).
/** @author __Elaina__ */
// #pragma GCC optimize("O2")
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI ((char)getchar())
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
template<class T> inline T listMax(const T& x) { return x; }
template<class T, class... Args> inline T listMax(const T& x, const Args&... args) {
return max(x, listMax(args...));
}
template<class T> inline T listMin(const T& x) { return x; }
template<class T, class... Args> inline T listMin(const T& x, const Args&... args) {
return min(x, listMin(args...));
}
} // namespace Elaina
using namespace Elaina;
const int Maxn = 4e5;
const int mod = 998244353;
inline void chkadd(int& x, int y) { if ((x += y) >= mod) x -= mod; }
inline int qkpow(int a, int n) {
int ret = 1;
for (; n; n >>= 1, a = (int)(1ll * a * a % mod))
if (n & 1) ret = (int)(1ll * ret * a % mod);
return ret;
}
inline int qkpow(int a, int n, int mod) {
int ret = 1;
for (; n; n >>= 1, a = (int)(1ll * a * a % mod))
if (n & 1) ret = (int)(1ll * ret * a % mod);
return ret;
}
namespace Poly {
int G[55];
int rev[Maxn << 2 | 2], n, invn;
inline void Initialization() {
rep (i, 1, 23) G[i] = qkpow(3, mod - 1 >> i);
}
struct _Initer {
_Initer() { Initialization(); }
} _initer;
inline void init(int len) {
for (n = 1; n < len; n <<= 1);
repf (i, 1, n) rev[i] = (rev[i >> 1] >> 1) | ((i & 1)? n >> 1: 0);
invn = qkpow(n, mod - 2);
}
inline void ntt(vset& f, int opt) {
f.resize(n);
repf (i, 1, n) if (i < rev[i]) swap(f[i], f[rev[i]]);
for (int p = 2, lev = 1; p <= n; p <<= 1, ++lev) {
int len = p >> 1, w = G[lev];
for (int k = 0; k < n; k += p) {
for (int i = k, buf = 1; i < k + len; ++i, buf = (int)(1ll * buf * w % mod)) {
int tmp = (int)(1ll * f[i + len] * buf % mod);
f[i + len] = (f[i] + mod - tmp) % mod;
f[i] = (f[i] + tmp) % mod;
}
}
}
if (opt == -1) { // DFT
reverse(f.begin() + 1, f.end());
for (int& x: f) x = (int)(1ll * x * invn % mod);
}
}
inline vset convolution(vset a, vset b) {
int len = (int)(a.size() + b.size()) - 1;
init(len), ntt(a, 0xcbdd1), ntt(b, 0xCBDD1);
for (int i = 0; i < n; ++i)
a[i] = (int)(1ll * a[i] * b[i] % mod);
return ntt(a, -1), a.resize(len), a;
}
inline void doubleIt(vset& a) {
int len = (int)(a.size() << 1) - 1;
init(len), ntt(a, 0xcbdd1);
for (int i = 0; i < n; ++i)
a[i] = (int)(1ll * a[i] * a[i] % mod);
ntt(a, -1), a.resize(len);
}
} // namespace NTT
int s[Maxn + 5], t[Maxn + 5], n, m, p, gp;
inline vset divide(int x) {
vset ret;
for (int i = 2; i * i <= x; ++i) if (x % i == 0) {
ret.push_back(i);
while (x % i == 0) x /= i;
}
if (x > 1) ret.push_back(x);
return ret;
}
inline int findroot(int p) {
vset div = divide(p - 1);
for (int i = 2; ; ++i) {
bool yes = true;
for (const auto& d: div) {
if (qkpow(i, (p - 1) / d, p) == 1) {
yes = false; break;
}
}
if (yes) return i;
}
}
inline void input() {
readin(p, n, m);
rep (i, 1, n) readin(s[i]);
rep (i, 1, m) readin(t[i]);
gp = findroot(p);
}
int invInp[Maxn + 5], pwof[Maxn + 5];
inline void prelude() {
invInp[0] = invInp[1] = 1;
repf (i, 2, p) invInp[i] = (int)(1ll * (p - p / i) * invInp[p % i] % p);
pwof[1] = 0;
for (int cur = gp, q = 1; cur != 1; cur = (int)(1ll * cur * gp % p), ++q)
pwof[cur] = q;
}
vset buc, pwbuc, pws;
inline void work() {
buc.resize(p);
rep (i, 1, n) ++buc[s[i]];
Poly::doubleIt(buc);
repf (i, p, p << 1) chkadd(buc[i - p], buc[i]);
buc.resize(p);
pwbuc.resize(p - 1), pws.resize(p - 1);
repf (i, 1, p) pwbuc[pwof[i]] = buc[i];
rep (i, 1, n) if (s[i]) pws[pwof[invInp[s[i]]]] = 1;
pwbuc = Poly::convolution(pwbuc, pws);
repf (i, p - 1, (p - 1 << 1) - 1) chkadd(pwbuc[i - (p - 1)], pwbuc[i]);
pwbuc.resize(p - 1);
}
bool inT[Maxn + 5];
inline void calc() {
rep (i, 1, m) inT[t[i]] = true;
int ans = 0;
repf (i, 1, p) {
int tar = (1ll * i * i + 1 + invInp[i]) % p;
if (tar) tar = p - tar;
if (inT[tar]) chkadd(ans, pwbuc[pwof[i]]);
}
writln(ans);
}
signed main() {
freopen("equation.in", "r", stdin);
freopen("equation.out", "w", stdout);
input();
prelude();
work();
calc();
return 0;
}
移花接木 / Link
暂时先咕掉。