2023武汉联训

2023省选武汉联测1

算是 \(Day1\)?

大佬好多,我好菜

A. 递归函数

不难发现答案至于第 \(0\) 列有关

由于我们只关心指数,所以把乘法变成加法

每个数的贡献就是一个组合数的形式

然后不太会了

学到两种做法,一种是把这个贡献看成多项式拉格朗日插值

另一种是维护一行的值,用矩阵进行转移

枚举 \(p^a\) 每次有 \(1\) 的贡献

转移只有 \(kp^a\) 特殊(有多的一个贡献)

于是求出 \(p^a\) 的矩阵然后快速幂,处理一下边界即可

对于 \(6 / 10\) 取较大的质因数

对于 \(4 / 8 / 9\) 把模数乘 \(2 / 3\) 最后直接除掉即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48);c = getchar();}while(isdigit(c));
	return x;
}

ull mod = 998444353;
int n, m, b, invm;
struct matrix{
	ull a[17][17];
	matrix(){memset(a, 0, sizeof(a));}
	void clear(){memset(a, 0, sizeof(a));}
	friend matrix operator *(const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i <= m; ++i)
			for(int k = 0; k <= m; ++k)
				if(x.a[i][k])
					for(int j = 0; j <= m; ++j)
						res.a[i][j] = (res.a[i][j] + x.a[i][k] * y.a[k][j]) % mod;
		return res;
	}
};
matrix qpow(matrix x, int y){
	matrix res;
	for(int i = 0; i <= m; ++i)res.a[i][i] = 1;
	for(; y; y >>= 1, x = x * x)if(y & 1)res = res * x;
	return res;
}
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
ull calc(int p){
	ull res = 0;
	for(int a = p; a <= n; a *= p){
		int x = n / a, y = n % a;
		matrix tmp, tr;
		for(int i = 1; i <= m; ++i)
			for(int j = i; j <= m; ++j)
				tmp.a[i][j] = 1;
		tmp.a[0][0] = 1; tr = qpow(tmp, a - 1);
		for(int i = 1; i <= m; ++i)tmp.a[0][i] = 1;
		tr = qpow(tr * tmp, x);
		for(int i = 1; i <= m; ++i)tmp.a[0][i] = 0;
		tr = tr * qpow(tmp, y);
		res = (res + tr.a[0][m]) % mod;
	}
	return res;
}
int main(){
	freopen("function.in","r",stdin);
	freopen("function.out","w",stdout);
	n = read(), m = read(), b = read();
	invm = 1; for(int i = 2; i <= m; ++i)invm = 1ll * invm * qpow(i, mod - 2) % mod;
	ull ans;
	switch(b){
		case 2: ans = calc(2); break;
		case 3: ans = calc(3); break;
		case 4: mod <<= 1; ans = calc(2) / 2; break;
		case 5: ans = calc(5); break;
		case 6: ans = calc(3); break; 
		case 7: ans = calc(7); break;
		case 8: mod *= 3; ans = calc(2) / 3; break;
		case 9: mod <<= 1; ans = calc(3) / 2; break;
		case 10: ans = calc(5); break;
	}
	printf("%d\n",ans);
	return 0;
}

B. 火力规划

只考虑一个方向,其他方向可以旋转,一样处理

一个士兵的贡献是一个等腰三角形,斜边在对角线上

斜边的限制可以用横纵坐标之和/差表示

于是用二维树状数组可以维护

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48);c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 5e3 + 55;
int n, q;
struct op{int opt, dir, x, y, r;}d[maxn * 20];
struct BIT{
	int t[maxn][maxn * 2];
	int lowbit(int x){return x & -x;}
	void add(int x, int y, int val){
		for(int a = x; a <= n; a += lowbit(a))
			for(int b = y; b <= n + n; b += lowbit(b))
				t[a][b] += val;
	}
	int query(int x, int y){
		int res = 0;
		for(int a = x; a; a -= lowbit(a))
			for(int b = y; b; b -= lowbit(b))
				res += t[a][b];
		return res;
	}
	void clear(){memset(t, 0, sizeof(t));}
}T1, T2;
int ans[maxn * 20];
int main(){
	freopen("planning.in","r",stdin);
	freopen("planning.out","w",stdout);
	n = read(), q = read();
	for(int i = 1; i <= q; ++i){
		d[i].opt = read();
		if(d[i].opt & 1){
			d[i].dir = read(), d[i].x = read(), d[i].y = read(), d[i].r = read();
			if(d[i].dir == 1){
				d[i].dir = 3;
			}else if(d[i].dir == 2){
				d[i].dir = 4;
			}else if(d[i].dir == 3){
				d[i].dir = 2;
			}else if(d[i].dir == 4){
				d[i].dir = 1;
			}
		}else{d[i].x = read(), d[i].y = read();}
	}
	for(int dir = 1; dir <= 4; ++dir){
		T1.clear(), T2.clear();
		for(int i = 1; i <= q; ++i)if(d[i].opt == 1 && d[i].dir == dir){
			int x = d[i].x, y = d[i].y, r = d[i].r;
			T1.add(x - r, x + y - r, 1);
			T1.add(x + 1, x + y - r, -1);
			T2.add(x - r, y + 1, -1);
			T2.add(x + 1, y + 1, 1);
		}else if(d[i].opt == 2){
			int x = d[i].x, y = d[i].y;
			ans[i] += T1.query(x, x + y) + T2.query(x, y);
		}
		for(int i = 1; i <= q; ++i)swap(d[i].x, d[i].y), d[i].x = n + 1 - d[i].x; 
	}
	for(int i = 1; i <= q; ++i)if(d[i].opt == 2)printf("%d\n",ans[i]);
	return 0;
}

C. 再生核希尔伯特空间

\(AC\) 自动机或者 \(SAM\) 都能做,做法类似

核心在于根号分治

口胡一下 \(AC\) 自动机, 代码没打完

考虑 \(len >= \sqrt m\) 的,考虑其每个结点会贡献 \(fail\) 树上到根的路径中经过的其他串的结尾,

修改该串结点的权值为 \(1\), 对整棵树做 \(DFS\) 求每个点的子树和即可

对于 \(len < \sqrt m\) 的,反过来考虑祖先对他的贡献,发现就是在每个点查询到根路径上颜色在 \([l, r]\) 之间的点有多少

离线下来差分搞一下

code

#include<bits/stdc++.h>

using namespace std;
namespace mycode{

	typedef long long ll;
	typedef unsigned long long ull;
	typedef pair<int, int> pii;

	int read(){
		int x = 0; char c = getchar();
		while(!isdigit(c))c = getchar();
		do{x = x * 10 + (c ^ 48);c = getchar();}while(isdigit(c));
		return x;
	}

	const int maxn = 5e5 + 55;

	char s[maxn];
	int n, q, ed[maxn], len[maxn], B, m;

	struct AC{
		int ch[maxn][26], fa[maxn], fail[maxn], cnt = 1;
		int insert(int len){
			int now = 1;
			for(int i = 1; i <= len; ++i){
				if(!ch[now][s[i] - 'a'])fa[ch[now][s[i] - 'a'] = ++cnt] = now;
				now = ch[now][s[i] - 'a'];
			}
			return now;
		}
		queue<int>q;
		vector<int>g[maxn];
		void build(){
			for(int i = 0; i < 26; ++i)if(ch[1][i])q.push(ch[1][i]), fail[ch[1][i]] = 1;
			else ch[1][i] = 1;
			while(!q.empty()){
				int x = q.front(); q.pop();
				for(int i = 0; i < 26; ++i)
					if(ch[x][i])q.push(ch[x][i]), fail[ch[x][i]] = ch[fail[x]][i];
					else ch[x][i] = ch[fail[x]][i];
			}
		}
		int val[maxn], sum[maxn];
		void add(int pos){
			while(pos){
				++val[pos];
				pos = fa[pos];
			}
		}
		void dfs(int x){
			sum[x] = val[x];
			for(int v : g[x])dfs(v), sum[x] += sum[v];
		}
		void modify(int x){

		}
		int query(int x){

		}
	}A;
	struct node{
		int op, pos;
		friend bool operator < (const node &x, const node &y){
			return x.pos < y.pos;
		}
	};
	int ans[maxn];
	vector<node>vec[maxn];

	int main(){
		// freopen("rkhs.in","r",stdin);
		// freopen("rkhs.out","w",stdout);
		n = read(), q = read();
		for(int i = 1; i <= n; ++i){
			scanf("%s",s + 1); len[i] = strlen(s + 1);
			ed[i] = A.insert(len[i]); m += len[i];
		}
		B = sqrt(m);
		for(int i = 1; i <= q; ++i){
			int a = read() - 1, b = read(), k = read();
			node l; l.pos = k; l.op = -1;
			node r; r.pos = k; r.op = 1;
			vec[a].push_back(l);
			vec[b].push_back(r); 
		}
		A.build();
		for(int i = 1; i <= n; ++i){
			if(len[i] >= B)A.add(ed[i]), A.dfs(1);
			for(node x : vec[i])ans[x.pos] += x.op * A.sum[ed[x.pos]];
		}
		for(int i = 1; i <= n; ++i){
			if(len[i] < B)A.modify(ed[i]);
			for(node x : vec[i])ans[x.pos] += x.op * A.query(ed[x.pos]);
		}	
		return 0;
	}
}

const int N = 1E5 + 10, S = 2e5 + 10, Q = 2E5 + 10;
using ll = long long;
struct acam {
#define fail(x) tr[x].fail
#define ch(x, p) tr[x].ch[p]
    struct node {
        int ch[26];
        int fail;
    } tr[S];
    int root = 1, tot = 1;
    int newnode() { return ++tot; }
    int insert(string s) {
        int u = root;
        for (auto c : s) {
            if (ch(u, c - 'a') == 0)
                ch(u, c - 'a') = newnode();
            u = ch(u, c - 'a');
        }
        return u;
    }
    vector<int> get_nds(string s) {
        vector<int> ret;
        ret.reserve(s.size());
        int u = root;
        for (auto c : s) {
            u = ch(u, c - 'a');
            assert(u);
            ret.push_back(u);
        }
        return ret;
    }
    void get_fail() {
        queue<int> q;
        for (int i = 0; i < 26; ++i) {
            if (ch(root, i))
                fail(ch(root, i)) = root, q.push(ch(root, i));
            else
                ch(root, i) = root;
        }
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = 0; i < 26; ++i) {
                if (ch(u, i))
                    fail(ch(u, i)) = ch(fail(u), i), q.push(ch(u, i));
                else
                    ch(u, i) = ch(fail(u), i);
            }
        }
    }
} AC;
struct tree {
    vector<int> e[S];
    ll f[S];
    void inse(int u, int v) { e[u].push_back(v); }
    int dfn[S], dfe[S], tim;
    void pre(int u) {
        dfn[u] = ++tim;
        for (int v : e[u]) {
            pre(v);
        }
        dfe[u] = tim;
    }
    void dfs(int u) {
        for (int v : e[u]) {
            dfs(v);
            f[u] += f[v];
        }
    }
} T;
int ds, db;
struct db_t {
    ll tag[S];
    int bel[S];
    ll a[S];
    int L[S], R[S];
    void init(int len) {
        ds = ceil(sqrt(len));
        db = ceil(1.0 * len / ds);
        for (int i = 1; i <= len; ++i) bel[i] = ceil(1.0 * i / ds);
        for (int i = 1; i <= db; ++i) L[i] = (i - 1) * ds + 1, R[i] = i * ds;
    }
    void add(int l, int r) {
        int bl = bel[l], br = bel[r];
        if (bl == br) {
            for (int i = l; i <= r; ++i) ++a[i];
            return;
        }
        for (int i = l; i <= R[bl]; ++i) ++a[i];
        for (int i = L[br]; i <= r; ++i) ++a[i];
        for (int i = bl + 1; i <= br - 1; ++i) ++tag[i];
    }
    int query(int x) { return a[x] + tag[bel[x]]; }
} segt;
struct que_t {
    int p, k, id, coef;
};
vector<que_t> sma, big;
ll ans[Q];
int edp[N];
int n, q;
string strs[N];
int bnd;
ll arr[N];
int main() {
    freopen("rkhs.in", "r", stdin);
    freopen("rkhs.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int sizsum = 0;
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> strs[i];
        edp[i] = AC.insert(strs[i]);
    }
    for (int i = 1; i <= n; ++i) sizsum += strs[i].length();
    bnd = sqrt(sizsum);
    AC.get_fail();
    for (int i = 2; i <= AC.tot; ++i) T.inse(AC.fail(i), i);
    T.pre(AC.root);
    segt.init(AC.tot);
    for (int i = 1; i <= q; ++i) {
        int l, r, k;
        cin >> l >> r >> k;
        if (strs[k].size() <= bnd) {
            sma.push_back({ l - 1, k, i, -1 });
            sma.push_back({ r, k, i, 1 });
        } else {
            big.push_back({ l - 1, k, i, -1 });
            big.push_back({ r, k, i, 1 });
        }
    }
    sort(big.begin(), big.end(), [&](que_t a, que_t b) { return a.k < b.k; });
    int nowid = -1;
    for (auto it : big) {
        if (it.k != nowid) {
            nowid = it.k;
            memset(arr, 0, sizeof(arr));
            memset(T.f, 0, sizeof(T.f));
            auto nds = AC.get_nds(strs[it.k]);
            for (auto it : nds) {
                T.f[it] = 1;
            }
            T.dfs(AC.root);
            for (int i = 1; i <= n; ++i) arr[i] = T.f[edp[i]];
            partial_sum(arr + 1, arr + 1 + n, arr + 1);
        }
        ll val = arr[it.p] * it.coef;
        ans[it.id] += val;
    }
    sort(sma.begin(), sma.end(), [&](que_t a, que_t b) { return a.p < b.p; });
    int rp = 0;
    for (auto it : sma) {
        while (rp < it.p) {
            ++rp;
            int u = edp[rp];
            segt.add(T.dfn[u], T.dfe[u]);
        }
        auto nds = AC.get_nds(strs[it.k]);
        ll val = 0;
        for (auto u : nds) {
            val += segt.query(T.dfn[u]);
        }
        val *= it.coef;
        ans[it.id] += val;
    }
    for (int i = 1; i <= q; ++i) {
        cout << ans[i] << "\n";
    }
    cout << endl;
    return 0;
}

2023省选武汉联测2

数据结构 (\(lxl\)) 专场

A. After God

神仙回滚莫队

首先很多位没用,离散化一下,可以把值域降到 \(O(n)\)

每次处理左端点在一块的询问

向右扩展可以直接暴力做,均摊下来是 \(O(n)\) 的(一个 \(1\) 只会在进位时贡献一次)

考虑如何快速维护向左扩展以及回滚操作

把值域按照当前块的值进行分块

维护每个块左端点开始 \(log n\) 个数(直接用 \(int\) 压位)

再维护之后的连续的 \(1\) 的个数

考虑左侧的扩展,每一块至多向上贡献一个 \(1\), 那么进位产生的影响只会在 \(log n\) 位和前缀 \(1\) 的位置产生影响

于是每次就可以在 \(\sqrt n\) 的复杂度内完成左端点的操作了

下面的代码是直接压位的暴力。。。。。

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar(); bool f = false;
	while(!isdigit(c))f = c  == '-', c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 1e6 + 55;
int mx;

int n, m, a[maxn], tmp[maxn], p[maxn], bl[maxn], B;
int ans[maxn];
struct node{
	int l, r, id;
	friend bool operator < (const node &x, const node &y){return bl[x.l] == bl[y.l] ? x.r < y.r : x.l < y.l;}
}d[maxn];
int cnt, pop[maxn];
ull tub[maxn];
void add(int pos){
	int x = a[pos], y = x >> 6;
	ull las = tub[y];
	tub[y] += (1ull << (x & 63));
	cnt -= pop[y];
	pop[y] = __builtin_popcountll(tub[y]);
	cnt += pop[y];
	if(((las & (1ull << 63)) == (1ull << 63)) && ((tub[y] & (1ull << 63)) == 0)){
		++y;
		do{
			cnt -= pop[y]; ++tub[y];
			pop[y] = __builtin_popcountll(tub[y]);
			cnt += pop[y];
		}while(tub[y] == 0 && (++y));
	}
}
void del(int pos){
	int x = a[pos], y = x >> 6;
	ull las = tub[y];
	tub[y] -= (1ull << (x & 63));
	cnt -= pop[y];
	pop[y] = __builtin_popcountll(tub[y]);
	cnt += pop[y];
	if(((las & (1ull << 63)) == 0) && ((tub[y] & (1ull << 63)) == (1ull << 63))){
		++y;
		do{
			cnt -= pop[y]; --tub[y];
			pop[y] = __builtin_popcountll(tub[y]);
			cnt += pop[y];
		}while((~tub[y] == 0) && (++y));
	}
}
void solve(){
	int l = d[1].l, r = d[1].l - 1;
	for(int i = 1; i <= m; ++i){
		while(l > d[i].l)--l, add(l);
		while(r < d[i].r)++r, add(r);
		while(l < d[i].l)del(l), ++l;
		while(r > d[i].r)del(r), --r;
		ans[d[i].id] = cnt;
	}
}
bool cmp(int x, int y){return a[x] < a[y];}
int main(){
	freopen("god.in","r",stdin);
	freopen("god.out","w",stdout);
	n = read(), m = read(); 
	for(int i = 1; i <= n; ++i)a[i] = tmp[i] = read(), p[i] = i;
	sort(tmp + 1, tmp + n + 1); sort(p + 1, p + n + 1, cmp);
	for(int i = n; i > 1; --i)tmp[i] = tmp[i] - tmp[i - 1];
	tmp[1] = 1;
	for(int i = 2; i <= n; ++i)tmp[i] = min(tmp[i], 18);
	for(int i = 2; i <= n; ++i)tmp[i] += tmp[i - 1];
	for(int i = 1; i <= n; ++i)a[p[i]] = tmp[i];
	mx = tmp[n] + 18;
	B = pow(n, 0.5); for(int i = 1; i <= n; ++i)bl[i] = (i + B - 1) / B;
	for(int i = 1; i <= m; ++i){
		d[i].l = read(), d[i].r = read(); d[i].id = i;
	}
	sort(d + 1, d + m + 1);
	solve();
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
	return 0;
}

B. 魔法少女

神仙线段树

按照 \(c\) 分块

块内询问直接山海经

考虑块间

\(a_i\) 表示前一个块后 \(len - i + 1\) 个数的后缀和

\(b_i\) 表示后一个块前 \(i\) 个数的前缀和

那么限制变成 \(i <= j\) \(b_i + a_j\) 的最大值

然后怎么维护一下。。。

C. 愚者之夜

分颜色处理,根号分治

对于出现次数小于根号的,枚举两个位置,查询区间最大最小值,得到当 \(l , r\) 满足一定条件时才会有贡献

那么变成了二维数点问题

对于出现次数大于根号的

用回滚莫队维护答案

2023省选武汉联测3

A. 回文串

可以发现如果两侧字符一样,则翻不翻转两侧是一样的

那么可以不停的去掉两侧相同的字符

发现剩下的两侧不同,那么翻转的区间一个端点必然是此时两个端点中的一个

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2e5 + 55, base1 = 29, base2 = 233, mod = 1e9 + 7;
int n, len;
char s[maxn], c[maxn];
ull bp1[maxn]; int bp2[maxn];
struct Hash{
	ull h1; int h2;
	Hash(){h1 = h2 = 0;}
	Hash(ull x, int y){h1 = x; h2 = y;}
	friend bool operator == (const Hash &x, const Hash &y){
		return x.h1 == y.h1 && x.h2 == y.h2;
	}
	friend Hash operator + (const Hash &x, const Hash &y){
		return Hash(x.h1 + y.h1, (x.h2 + y.h2) % mod);
	}
	Hash operator * (const int l){
		return Hash(h1 * bp1[l], 1ll * h2 * bp2[l] % mod);
	}
}pre[maxn], suf[maxn];
Hash get_pre(int l, int r){return Hash(pre[r].h1 - pre[l - 1].h1 * bp1[r - l + 1], (pre[r].h2 - 1ll * pre[l - 1].h2 * bp2[r - l + 1] % mod + mod) % mod);}
Hash get_suf(int l, int r){return Hash(suf[l].h1 - suf[r + 1].h1 * bp1[r - l + 1], (suf[l].h2 - 1ll * suf[r + 1].h2 * bp2[r - l + 1] % mod + mod) % mod);}
void sol(){
	for(int i = 1; i <= len; ++i)pre[i] = Hash(pre[i - 1].h1 * base1 + (c[i] - 'a'), (1ll * pre[i - 1].h2 * base2 + (c[i] - 'a')) % mod);
	suf[len + 1].h1 = suf[len + 1].h2 = 0;
	for(int i = len; i >= 1; --i)suf[i] = Hash(suf[i + 1].h1 * base1 + (c[i] - 'a'), (1ll * suf[i + 1].h2 * base2 + (c[i] - 'a')) % mod);
}
void solve(){
	n = read(); scanf("%s",s + 1);
	int L = 1, R = n;
	while(L <= R && s[L] == s[R])++L, --R;
	if(L > R){printf("1 1\n");return;}
	len = R - L + 1;
	for(int i = L; i <= R; ++i)c[i - L + 1] = s[i];
	sol();
	for(int i = 1; i <= len; ++i){
		int j = len - i;
		if(get_suf(1, i) * j + get_pre(i + 1, len) == get_suf(i + 1, len) * i + get_pre(1, i)){
			printf("%d %d\n",L, L + i - 1); return;
		}
		if(get_pre(1, j) * i + get_suf(j + 1, len) == get_pre(j + 1, len) * j + get_suf(1, j)){
			printf("%d %d\n",j + L, R); return;
		}
	}
	printf("-1 -1\n");
}

int main(){
	// freopen("palindrome.in","r",stdin);
	// freopen("palindrome.out","w",stdout);
	bp1[0] = bp2[0] = 1;
	for(int i = 1; i <= 1e5 + 5; ++i)bp1[i] = bp1[i - 1] * base1;
	for(int i = 1; i <= 1e5 + 5; ++i)bp2[i] = 1ll * bp2[i - 1] * base2 % mod;
	int t = read(); for(int i = 1; i <= t; ++i)solve();
	return 0;
}

B. 国际象棋

特批 \(n <=3 || m <= 3\)

构造 \(6 >= n, m >= 4\)

更大的可以拆分成 \(n, m \in[4, 6]\)

code
#include<bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
struct node{
	int a, b, c, d;
	node(){}
	node(int e, int f, int g, int h){a = e; b = f; c = g; d = h;}
	node add(int x, int y){return node{a + x, b + y, c + x, d + y};}
	node radd(int x, int y){return node{b + x, a + y, d + x, c + y};}
};
vector<node>tabe[7][7], ans;
void case24(int x, int y){
	ans.push_back({x, y, x + 1, y + 2});
	ans.push_back({x + 1, y, x, y + 2});
	ans.push_back({x, y + 1, x + 1, y + 3});
	ans.push_back({x + 1, y + 1, x, y + 3});
}
void case42(int x, int y){
	ans.push_back({x, y, x + 2, y + 1});
	ans.push_back({x, y + 1, x + 2, y});
	ans.push_back({x + 1, y, x + 3, y + 1});
	ans.push_back({x + 1, y + 1, x + 3, y});
}
void pre(){
	tabe[3][3] = { { 1, 1, 3, 2 }, { 1, 2, 3, 3 }, { 1, 3, 2, 1 }, { 2, 3, 3, 1 } };
	tabe[3][4] = { { 1, 1, 3, 2 }, { 1, 2, 2, 4 }, { 1, 3, 2, 1 }, { 1, 4, 3, 3 }, { 2, 2, 3, 4 }, { 2, 3, 3, 1 } };
	tabe[3][5] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 3, 4 }, { 2, 3, 3, 5 }, { 2, 5, 3, 3 } };
	tabe[3][6] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 1, 6, 3, 5 }, { 2, 4, 3, 6 }, { 2, 5, 3, 3 }, { 2, 6, 3, 4 } };
	tabe[4][5] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 2, 4, 4, 5 }, { 2, 5, 4, 4 }, { 3, 3, 4, 1 }, { 3, 4, 4, 2 }, { 3, 5, 4, 3 } };
	tabe[4][6] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 1, 6, 2, 4 }, { 2, 5, 4, 6 }, { 2, 6, 4, 5 }, { 3, 3, 4, 1 }, { 3, 4, 4, 2 }, { 3, 5, 4, 3 }, { 3, 6, 4, 4 } };
	tabe[5][5] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 2, 4, 4, 5 }, { 2, 5, 3, 3 }, { 3, 4, 4, 2 }, { 3, 5, 5, 4 }, { 4, 1, 5, 3 }, { 4, 3, 5, 1 }, { 4, 4, 5, 2 } };
	tabe[5][6] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 1, 6, 2, 4 }, { 2, 5, 3, 3 }, { 2, 6, 4, 5 }, { 3, 4, 4, 2 }, { 3, 5, 5, 6 }, { 3, 6, 5, 5 }, { 4, 1, 5, 3 }, { 4, 3, 5, 1 }, { 4, 4, 5, 2 }, { 4, 6, 5, 4 } };
	tabe[6][6] = { { 1, 1, 3, 2 }, { 1, 2, 3, 1 }, { 1, 3, 2, 1 }, { 1, 4, 2, 2 }, { 1, 5, 2, 3 }, { 1, 6, 2, 4 }, { 2, 5, 3, 3 }, { 2, 6, 3, 4 }, { 3, 5, 4, 3 }, { 3, 6, 5, 5 }, { 4, 1, 6, 2 }, { 4, 2, 6, 1 }, { 4, 4, 5, 2 }, { 4, 5, 5, 3 }, { 4, 6, 6, 5 }, { 5, 1, 6, 3 }, { 5, 4, 6, 6 }, { 5, 6, 6, 4 } };
}
int n, m;
void solve(int x, int y, int a, int b){
	if(a == 4 && b == 4){case24(x + 1, y + 1); case24(x + 3, y + 1); return;}
	if(a > b)for(auto v : tabe[b][a])ans.push_back(v.radd(x, y));
	else for(auto v : tabe[a][b])ans.push_back(v.add(x, y));
}
void solve1(){
	if(n == 1 || m == 1)return;
	if(n == 2){
		for(int i = 1; i <= m; i += 4)if(i + 3 <= m)case24(1, i);
		if(m % 4 == 3){ans.push_back({1, m, 2, m - 2}); ans.push_back({1, m - 2, 2, m});}
	}
	else{
		for(int i = 1; i <= n; i += 4)if(i + 3 <= n)case42(i, 1);
		if(n % 4 == 3){ans.push_back({n, 1, n - 2, 2});ans.push_back({n, 2, n - 2, 1});}
	}
}
void solve2(){
	int rn = n % 4, rm = m % 4;
	if(rn < 3) rn += 4;
	if(rm < 3) rm += 4;
	for(int i = 1; i + 3 <= n - rn; i += 4)
		for(int j = 1; j + 3 <= m - rm; j += 4)
			solve(i - 1, j - 1, 4, 4);
	if(rm)for(int i = 1; i + 3 <= n - rn; i += 4)solve(i - 1, m - rm, 4, rm);
	if(rn)for(int i = 1; i + 3 <= m - rm; i += 4)solve(n - rn, i - 1, rn, 4);
	if(rn && rm)solve(n - rn, m - rm, rn, rm);
}
void solve(){
	n = read(), m = read();
	if(n < 3 || m < 3)solve1();
	else solve2();
	printf("%d\n",(int)ans.size());
	for(auto x : ans){
		printf("%d %d %d %d\n",x.a, x.b, x.c, x.d);
	}
	ans.clear();
}

int main(){
	freopen("knight.in","r",stdin);
	freopen("knight.out","w",stdout);
	pre(); int t = read();
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

C. 嘉心糖

建补图,问题变成找最大独立集

给边定向,标号小的指向标号大的,

可以证明如果 \(i < j < k\) \(i - j\) 无边 \(j - k\) 无边,则 $i ,k $ 无边

于是能证明答案为最长反链?

然后根据狄尔沃斯定理要求最小链覆盖

于是拆点,答案为 \(n - 最大匹配\)

然后优化匈牙利算法

复杂度分析不会

参考https://www.cnblogs.com/whx1003/p/15597645.html

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5e5 + 55;
int n, m, deg[maxn], a[maxn], p[maxn];
int f[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool cmp(int x, int y){return deg[x] < deg[y];}
int ra[maxn], rb[maxn];
set<int, greater<int>>s;
set<int>del[maxn];
bool dfs(int x){
	int find = x + 1;
	while(find = fa(find), find <= n){
		if(del[x].count(find)){++find; continue;}
		else{
			f[find] = find + 1;
			if(!rb[find] || dfs(rb[find])){
				ra[x] = find; rb[find] = x;
				return true;
			}
		}
	}
	return false;
}
int main(){
	freopen("sugar.in","r",stdin);
	freopen("sugar.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)deg[i] = n - i, p[i] = a[i] = i;
	for(int i = 1; i <= m; ++i){
		int x = read();
		del[min(a[x], a[x + 1])].insert(max(a[x], a[x + 1]));
		--deg[min(a[x], a[x + 1])]; swap(a[x], a[x + 1]);
	}
	for(int i = 1; i <= n; ++i)s.insert(i);
	sort(p + 1, p + n + 1, cmp);
	int ans = 0;
	for(int i = 1; i <= n; ++i){
		int now = p[i];
		if(deg[now] > ans)for(int v : s)
			if(v > now && !del[now].count(v)){
				ra[now] = v; rb[v] = now;
				s.erase(v);
				++ans;
				break;
			}	
	}
	for(int i = 1; i <= n; ++i)if(!ra[i]){
		for(int j = 1; j <= n + 1; ++j)f[j] = j;
		ans += dfs(i);
	}
	printf("%d\n", n - ans);
	return 0;
}

2023省选武汉联测4

image
image

A. 挑战NPC

虽然但是,直接BK能过

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

int read(){
	int x = 0; bool f = false; char c = getchar();
	while(!isdigit(c))f = c == '-', c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 1e5 + 55;
int n, m, ans = 1;
struct node{int x, y;}d[maxn];
mt19937 rd((ull)&maxn);
ll sq(ll x){return x * x;}
ll dis2(const node &x, const node &y){return sq(x.x - y.x) + sq(x.y - y.y);}
vector<pii>e[maxn];
bool mp[205][205];
int p[maxn];
int id[205][205], dp[maxn];
void dfs(int si, int num){
	if(si == 0){
		if(num > ans)ans = num;
		return;
	}
	for(int i = 1; i <= si; ++i){
		if(si - i + 1 + num <= ans)return;
		int now = id[num][i];
		if(dp[now] + num <= ans)return;
		int cnt = 0;
		for(int j = i + 1; j <= si; ++j)
			if(mp[now][id[num][j]])
				id[num + 1][++cnt] = id[num][j];
		dfs(cnt, num + 1);
	}
}
void solve(){
	ans = 0;
	for(int i = n; i >= 1; --i){
		int cnt = 0;
		for(int j = i + 1; j <= n; ++j)
			if(mp[i][j])id[1][++cnt] = j;
		dfs(cnt, 1);
		dp[i] = ans;
	}
}

void add(int d){
	for(pii v : e[d]){
		mp[v.first][v.second] = true;
		mp[v.second][v.first] = true;
	}
	shuffle(p + 1, p + n + 1, rd);
	solve();
}
int main(){
	freopen("challenge.in","r",stdin);
	freopen("challenge.out","w",stdout);
	n = read(); m = read();
	for(int i = 1; i <= n; ++i)p[i] = i;
	for(int i = 1; i <= n; ++i)d[i].x = read(), d[i].y = read();
	for(int i = 1; i <= n; ++i)
		for(int j = i + 1; j <= n; ++j){
			ll d2 = dis2(d[i], d[j]);
			int v = max((int)ceil(sqrt(d2)) - 5, 1);
			while(1ll * v * v < d2)++v;
			if(v <= m)e[v].push_back(pii(i, j));
		}
	for(int d = 0; d <= m; ++d){
		if(e[d].size())add(d);
		printf("%d ",ans);
	}
	printf("\n");
	return 0;
}

B. 糖果大赛

类打表题

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 205, mod = 998244353;
int n, a[maxn];
void sort_and_unique(vector<int>&x){
	sort(x.begin(), x.end());
	x.resize(unique(x.begin(), x.end()) - x.begin());
}
map<vector<int>, bool>mp;
bool dfs(vector<int>x){
	sort_and_unique(x);
	if(x.size() == 0)return true;
	if(x == vector<int>{4, 8})return false;
	if(x == vector<int>{1})return false;
	if(x == vector<int>{2})return false;
	for(auto v : x)if(v % 12)return true;
	if(mp.count(x))return mp[x];
	for(int s = 1; s <= x.back(); ++s){
		vector<int>tmp;
		for(int v : x)if(v % s)tmp.push_back(v % s);
		if(dfs(tmp) == false)return mp[x] = true;
	}
	return mp[x] = false;
}

int calc(vector<int>x){
	vector<int> f(1 << x.size(), 0); f[0] = 1;
	for(int i = 1; i <= n; ++i){
		vector <int> g(f.size(), 0);
		for(int j = 0; j < x.size(); ++j)if(a[i] >= x[j]){
			for(int s = 0; s < f.size(); ++s)if(f[s]){
				(g[s | (1 << j)] += f[s]) %= mod;
			}
		}
		f.swap(g);
	}
	return f.back();
}


int main(){
	freopen("candy.in","r",stdin);
	freopen("candy.out","w",stdout);
	n = read(); 
	for(int i = 1; i <= n; ++i)a[i] = read();
	vector<vector<int>>ban;
	ban.push_back(vector<int>{1});
	ban.push_back(vector<int>{2});
	ban.push_back(vector<int>{4, 8});
	for(int s = 1; s < (1 << 16); ++s){
		vector<int>tmp;
		for(int i = 0; i <= 15; ++i)if(s >> i & 1)tmp.push_back((i + 1) * 12);
		if(!dfs(tmp))ban.push_back(tmp);
	}
	int ans = 1;
	for(int i = 1; i <= n; ++i)ans = 1ll * ans * a[i] % mod;
	for(auto v : ban)ans = (ans + mod - calc(v)) % mod;
	printf("%d", ans);
	return 0;
}

C. 聚会

神奇贪心

code
#include<bits/stdc++.h>

using namespace std;

#define int long long 
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

int n, m;
const int maxn = 3e5 + 55;
vector<pii>v[maxn];
ll a[maxn], b[maxn];
int p;
bool check(ll mid, ll val){
	for(int i = 1; i <= n; ++i)b[i] = 0;
	priority_queue<pii>q;
	int s = 0;
	for(int i = 1; i <= p; ++i){
		for(auto x : v[i])if(x.first > p)q.push(x);
		while(a[i] - s + val - s > mid){
			if(q.empty())return false;
			pii x = q.top(); q.pop();
			int del = min(x.second, (a[i] + val - mid + 1) / 2 - s);
			s += del; x.second -= del;
			b[p + 1] -= del;
			b[x.first] += del * 2;
			if(x.second)q.push(x);
		}
	}
	for(int i = p + 1; i <= n; ++i){
		b[i] += b[i - 1];
		if(a[i] + b[i] > mid)return false;
	}
	return true;	
}
signed main(){
	freopen("party.in","r",stdin);
	freopen("party.out","w",stdout);
	n = read(); m = read();
	for(int i = 1; i <= m; ++i){
		int l = read(), r = read(), x = read();
		if(l > r)swap(l, r);
		a[l] += x; a[r] -= x; v[l].push_back(pii(r, x));
	}
	for(int i = 1; i <= n; ++i)a[i] += a[i - 1];
	p = max_element(a, a + n + 1) - a;
	ll l = 1, r = a[p], ans = r;
	while(l <= r){
		ll mid = (l + r) >> 1;
		if(check(mid, a[p] - mid) || check(mid, a[p] - mid + 1))r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%lld\n",ans);
	return 0;
}

2023省选武汉联测5

A. 巴萨

CF1510J

只保留蓝色格子和两个蓝色段之间的一个红色格子,剩下的红色格子可以自由移动

对于一个蓝色段,他的位置取决于左右可自由移动的格子数量

设最靠左时为 \([l, r]\)\(B\) 个可移动格子,那么最靠右为 \([l + B, r + B]\)

一定染色的为 \([l + B, r]\)

枚举 \(B\), 每一段向前拓展,然后可以加上长度不超过 \(B\) 的,进行构造

证明 \(B\) 枚举到 \(3\) 即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int maxn = 5e6 + 55;
int n; char s[maxn];
vector<int>L;
bool ans[maxn];
bool check(int k){
	for(int l = 1, r = 1; l <= n; l = r + 1, r = l)if(ans[l] == false){
		while(ans[r + 1] == false) ++r;
		int len = r - l + 1; 
		if(l != 1)len -= r == n ? k : 1;
		if(k == 0){if(len)return false;}
		if(k == 1){if(len & 1)return false;}
		if(k >= 2){if(len == 1)return false;}
	}
	return true;
}
void print(int k){
	for(int l = 1, r = 1; l <= n; l = r + 1, r = l)if(ans[l] == false){
		while(ans[r + 1] == false) ++r;
		int len = r - l + 1; 
		if(l != 1){if(r != n) ++l, --len; else len -= k, l += k;}
		if(len == 0)continue;
		if(len == 1)assert(0);
		if(len & 1){
			ans[l] = ans[l + 1] = true;
			l += 3; len -= 3;
		}
		while(len){ans[l] = true; l += 2; len -= 2;}
	}
	printf("Yes\n");
	for(int i = 1; i <= n; ++i)if(ans[i])printf("B"); else printf("R"); printf("\n");
}
int main(){
	freopen("barca.in","r",stdin);
	freopen("barca.out","w",stdout);
	scanf("%d",&n); scanf("%s",s + 1);
	for(int l = 1, r = l; l <= n; l = r + 1, r = l)if(s[l] == '1'){
		while(s[r + 1] == '1' && r < n)++r;
		L.push_back(l);
		for(int i = l; i <= r; ++i)ans[i] = true;
	}
	ans[n + 1] = true;
	if(L.empty()){
		printf("Yes\n");
		for(int i = 1; i <= n; ++i)printf("R"); return 0;
	}
	for(int k = 0; k <= 3; ++k){
		if(check(k)){print(k); return 0;}
		for(int &x : L){
			--x; 
			if(ans[x] || x == 0 || ans[x - 1]){printf("No\n"); return 0;}
			ans[x] = true;
		}
		if(ans[n - k])break;
	}
	printf("No\n");
	return 0;
}

B. 拜仁

C. 大巴黎

CF794G

image

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int maxn = 4e6 + 55, mod = 1e9 + 7;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
char a[maxn], b[maxn];
int n, ans, p2[maxn], fac[maxn], ifac[maxn], f[maxn];
int C(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int main(){
	// freopen("bayern.in","r",stdin);
	// freopen("bayern.out","w",stdout);
	scanf("%s%s%d",a + 1,b + 1, &n);
	int la = strlen(a + 1), lb = strlen(b + 1);
	fac[0] = ifac[0] = p2[0] = 1;
	for(int i = 1; i <= max(max(n + 1, la), lb); ++i)p2[i] = p2[i - 1] * 2 % mod;
	for(int i = 1; i <= la + lb; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[la + lb] = qpow(fac[la + lb], mod - 2);
	for(int i = la + lb - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	for(int i = n; i >= 1; --i){
		f[i] = 1ll * (n / i) * (n / i) % mod;
		for(int j = i + i; j <= n; j += i)
			f[i] = (f[i] - f[j] + mod) % mod;
	}
	int c = 0;
	for(int i = 1; i <= n; ++i)c = (1ll * f[i] * p2[i] + c) % mod;
	int da = 0, db = 0, qa = 0, qb = 0;
	for(int i = 1; i <= la; ++i)if(a[i] == 'D')++da; else if(a[i] == 'F')++db; else ++qa;
	for(int i = 1; i <= lb; ++i)if(b[i] == 'D')--da; else if(b[i] == 'F')--db; else ++qb;
	for(int d = -qb; d <= qa; ++d){
		int tr = C(qa + qb, qb + d), ta = da + d, tb = db + qa - qb - d;
		if(ta == 0 && tb == 0){ans = (1ll * tr * c + ans) % mod; continue;}
		if(1ll * ta * tb < 0){
			ta = abs(ta); tb = abs(tb);
			ans = (1ll * tr * (p2[n / (max(ta, tb) / __gcd(ta, tb)) + 1] - 2 + mod) + ans) % mod;
		}
	}
	if(la == lb){
		bool flag = true; int cnt = 0;
		for(int i = 1; i <= la; ++i)if(a[i] != '?' && b[i] != '?' && a[i] != b[i])flag = false;
		else if(a[i] == '?' && b[i] == '?')++cnt;
		if(flag){
			ans = (ans - 1ll * p2[cnt] * c % mod + mod) % mod;
			ans = (ans + 1ll * p2[cnt] * (p2[n + 1] - 2 + mod) % mod * (p2[n + 1] - 2 + mod)) % mod;
		}
	}
	printf("%d\n", ans);
	return 0;
}

2023省选武汉联测6

image

A. 线性代数

解释一下系数\(2^{k - 1}\),就是看有奇数/偶数个主元在这一位为 \(1\), 因为最大值是所有的异或起来,所以奇数为 \(1\) 偶数为 \(0\)

code
#include<bits/stdc++.h>

using namespace std;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 55, mod = 1e9 + 7;
int f[maxn][maxn][2], n;
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
	freopen("algebra.in","r",stdin);
	freopen("algebra.out","w",stdout);
	n = read(); f[30][0][1] = 1;
	for(int i = 30; i > 0; --i){
		int now = (n >> (i - 1)) & 1;
		for(int j = 0; j <= 30 - i; ++j)
			for(int k = 0; k <= 1; ++k)if(f[i][j][k]){	
				if(!k || now){
					add(f[i - 1][j + 1][k && now == 1], f[i][j][k]);
					if(j)add(f[i - 1][j][k && now == 1], 1ll * (1 << max(j - 1, 0)) * f[i][j][k] % mod);
				}
				add(f[i - 1][j][k && now == 0], 1ll * (1 << max(j - 1, 0)) * f[i][j][k] % mod);
			}
	}
	int ans = 0;
	for(int i = 0; i <= 30; ++i)add(ans, f[0][i][0]), add(ans, f[0][i][1]);
	printf("%d\n",ans);
	return 0;
}

B. 森林

code
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1e5 + 55;
struct seg{
	struct node{
		int mi, cnt, tag;
	}t[maxn << 2 | 1];
	void clear(int x, int l, int r){
		t[x].mi = t[x].tag = 0; t[x].cnt = r - l + 1;
		if(l == r)return;
		int mid = (l + r) >> 1;
		clear(x << 1, l, mid);
		clear(x << 1 | 1, mid + 1, r);
	}
	void push_up(int x){
		t[x].cnt = 0; t[x].mi = min(t[x << 1].mi, t[x << 1 | 1].mi);
		if(t[x << 1].mi == t[x].mi)t[x].cnt += t[x << 1].cnt;
		if(t[x << 1 | 1].mi == t[x].mi)t[x].cnt += t[x << 1 | 1].cnt;
	}
	void upd(int x, int val){
		t[x].mi += val;
		t[x].tag += val;
	}
	void push_down(int x){if(t[x].tag)upd(x << 1, t[x].tag), upd(x << 1 | 1, t[x].tag), t[x].tag = 0;}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R)return upd(x, val);
		int mid = (l + r) >> 1; push_down(x);
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
}T;
vector<pii>e, op[maxn];
vector<int>g[maxn];
int n, fa[maxn][19], dfn[maxn], tim, dfnr[maxn], dep[maxn];
void dfs(int x){
	dfn[x] = ++tim;
	for(int i = 1; i <= 18; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int v : g[x])if(v != fa[x][0]){
		fa[v][0] = x; dep[v] = dep[x] + 1;
		dfs(v);
	}
	dfnr[x] = tim;
}
int down(int u, int v){
	for(int i = 18; i >= 0; --i)if(dep[fa[v][i]] > dep[u])v = fa[v][i];
	return v;
}
int LCA(int u, int v){
	if(dep[u] < dep[v])swap(u, v);
	for(int i = 18; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
	if(u == v)return u;
	for(int i = 18; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}
ll ans;
void solve(int x){
	for(pii v : op[x])T.modify(1, 1, n, dfn[v.first], dfnr[v.first], -v.second);
	ans += T.t[1].cnt;
	for(int v : g[x])if(v != fa[x][0]){
		T.modify(1, 1, n, 1, n, 1);
		T.modify(1, 1, n, dfn[v], dfnr[v], -2);
		solve(v);
		T.modify(1, 1, n, 1, n, -1);
		T.modify(1, 1, n, dfn[v], dfnr[v], 2);	
	}
	for(pii v : op[x])T.modify(1, 1, n, dfn[v.first], dfnr[v.first], v.second);
}
void sol(){
	n = read();
	T.clear(1, 1, n); tim = 0; e.clear();
	for(int i = 1; i <= n; ++i)g[i].clear(), op[i].clear();
	for(int i = 1; i < n; ++i){int u = read(), v = read(); e.push_back(pii(u, v));}
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs(1);
	for(pii now : e){
		int u = now.first, v = now.second;
		int lca = LCA(u, v);
		if(lca == u || lca == v){
			if(lca == v)swap(u, v);
			int t = down(u, v);
			op[1].push_back({v, 1});
			op[t].push_back({v, -1});
			op[v].push_back({1, 1});
			op[v].push_back({t, -1});
		}else{op[u].push_back({v, 1}); op[v].push_back({u, 1});}
	}
	for(int i = 1; i <= n; ++i)T.modify(1, 1, n, dfn[i], dfn[i], dep[i]);
	ans = n; solve(1); ans /= 2; printf("%lld\n",ans);
}

int main(){
	freopen("forest.in","r",stdin);
	freopen("forest.out","w",stdout);
	int t = read(); for(int i = 1; i <= t; ++i)sol();	
	return 0;
}

C. 字符串

事实上预处理 \(<= B\) 的部分过不了,常数大,而且这题时限不太合理

直接做加上记忆化能过,构造数据好像不太容易卡掉

code
#include<bits/stdc++.h>
using namespace std;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5e5 + 55, inf = 0x3f3f3f3f;
int n, q, rt[maxn], pos[maxn];
char s[maxn];
struct seg{
	int cnt;
	struct node{int mi, l, r;}t[maxn * 30];
	void push_up(int x){t[x].mi = min(t[t[x].l].mi, t[t[x].r].mi);}
	int insert(int x, int l, int r, int pos){
		int now = ++cnt; t[now] = t[x];
		t[now].mi = min(t[now].mi, l);
		if(l == r)return now;
		int mid = (l + r) >> 1;
		if(pos <= mid)t[now].l = insert(t[x].l, l, mid, pos);
		else t[now].r = insert(t[x].r, mid + 1, r, pos);
		push_up(now); return now;
	}
	int query(int x, int l, int r, int L){
		if(!x)return inf;
		if(L <= l)return t[x].mi;
		int mid = (l + r) >> 1;
		if(L > mid)return query(t[x].r, mid + 1, r, L);
		int res = query(t[x].l, l, mid, L); 
		if(res != inf)return res;
		return query(t[x].r, mid + 1, r, L);
	}
	int merge(int x, int y, int l, int r){
		if(!x || !y)return x | y;
		int now = ++cnt, mid = (l + r) >> 1;
		t[now].l = merge(t[x].l, t[y].l, l, mid);
		t[now].r = merge(t[x].r, t[y].r, mid + 1, r);
		push_up(now); return now;
	}
}T;
unordered_map<int, int>mp[maxn];
int sol(int now, int len){
	if(mp[now].count(len))return mp[now][len];
	int res = 0, p = T.query(rt[now], 1, n, 1);
	while(p <= n){
		++res;
		p = T.query(rt[now], 1, n, p + len - 1);
	}
	return mp[now][len] = res;
}
struct SAM{
	struct node{
		int fa, ch[26], len;
	}d[maxn];
	int las = 1, cnt = 1;
	void insert(int c){
		int fa = las, now = las = ++cnt;
		d[now].len = d[fa].len + 1;
		for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
		if(!fa)d[now].fa = 1;
		else{
			int x = d[fa].ch[c];
			if(d[x].len == d[fa].len + 1)d[now].fa = x;
			else{
				int clone = ++cnt;
				d[clone] = d[x];
				d[clone].len = d[fa].len + 1;
				d[x].fa = d[now].fa = clone;
				for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
			}
		}
	}
	vector<int>g[maxn];
	int fa[maxn][20];
	void dfs(int x){
		for(int i = 1; i <= 19; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
		for(int v : g[x])if(v != fa[x][0]){
			fa[v][0] = x;
			dfs(v);
			rt[x] = T.merge(rt[x], rt[v], 1, n);
		}
	}
	void build(){
		for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
		dfs(1);
	}
	int solve(int l, int r){
		int len = r - l + 1, now = pos[r];
		for(int i = 19; i >= 0; --i)if(d[fa[now][i]].len >= len)now = fa[now][i];
		return sol(now, len);
	}
}S;
int main(){
	// freopen("string.in","r",stdin);
	// freopen("string.out","w",stdout);
	n = read(), q = read();
	scanf("%s",s + 1);
	T.t[0].mi = inf;
	for(int i = 1; i <= n; ++i){
		S.insert(s[i] - 'a');
		rt[S.las] = T.insert(rt[S.las], 1, n, i);
		pos[i] = S.las;
	}
	S.build();
	for(int i = 1; i <= q; ++i){
		int l = read(), r = read();
		if(l == r)printf("-1\n");
		else printf("%d\n",S.solve(l, r));
	}
	return 0;
}

2023省选武汉联测7

A. 动点(point)

引用题解

## 题解 - 山路动点(point)

首先不管修改操作,只考虑询问。不难想到用矩阵处理:

- 沿着向量 $v$ 平移:$x \leftarrow x+v_x$,$y \leftarrow  y+v_y$
- 逆时针旋转 $\theta$:$x \leftarrow x\cos \theta -y \sin \theta$,$y \leftarrow y \cos \theta + x \sin \theta$
- 投影到方向 $v$ 上:点乘 $v$ 后数乘 $v$,再数乘一个 $\frac{1}{|v|^2}$

以上都可以表示为向量 $(x,y,1)$ 左乘矩阵,而两类指令都可以表示为上面的过程的组合。因此考虑线段树维护矩阵乘积。

再考虑如何把操作加入。一个基本的思想是,操作时我们不显式地更改指令,而是尝试更改坐标系。对于平移操作,我们完全可以先将点$P$ 沿着逆方向平移,完成指令后再沿着正方向平移回来。写成矩阵形式就是把原来的指令矩阵 $M$ 变成 $DMD^{-1}$,其中 $D$ 为沿着向量 $(u,v)$ 平移对应的矩阵。这个很容易往线段树上打个乘法标记维护。对于翻折操作也是差不多的,我们可以尝试先把点翻过去,执行完指令再翻回来。需要注意的是在点翻到镜像位置之后,原有的旋转操作就需要把逆时针转变成顺时针转了。不过这还是很好办,我们对旋转维护 $M,M'$ 分别表示逆时针、顺时针转对应的矩阵即可。那么翻折操作就是把 $M$ 变为 $RM'R$,把 $M'$ 变为 $RMR$,其中 $R$ 是翻折对应的矩阵。

这题没怎么卡精度,应该不需要 `long double` 就能过。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; bool f = false; char c = getchar();
	while(!isdigit(c))f = c == '-', c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 1e5 + 55, mod = 998244353;
const double Pi = acos(-1);
int n, m;
struct matrix{
	double a[3][3];
	matrix(){memset(a, 0, sizeof(a));}
	void clear(){memset(a, 0, sizeof(a));}
	void I(){clear(); for(int i = 0; i < 3; ++i)a[i][i] = 1;}
	friend matrix operator + (const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i < 3; ++i)
			for(int j = 0; j < 3; ++j)
				res.a[i][j] = x.a[i][j] + y.a[i][j];
		return res;
	}
	friend matrix operator - (const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i < 3; ++i)
			for(int j = 0; j < 3; ++j)
				res.a[i][j] = x.a[i][j] - y.a[i][j];
		return res;
	}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i < 3; ++i)
			for(int k = 0; k < 3; ++k)
				for(int j = 0; j < 3; ++j)
					res.a[i][j] += x.a[i][k] * y.a[k][j];
		return res;
	}
};
matrix trans(double u, double v){
	matrix res; res.I();
	res.a[2][0] = u; res.a[2][1] = v;
	return res;
}
matrix rota(double the){
	matrix res;
	double c = cos(the), s = sin(the);
	res.a[0][0] = res.a[1][1] = c;
	res.a[1][0] = -s; res.a[0][1] = s;
	res.a[2][2] = 1; 
	return res;
}
matrix tr(double a, double b, double c, bool op){
	double fm = a * a + b * b, fz = a * a - b * b;
	matrix res; res.a[2][2] = 1;
	if(op){
		res.a[0][0] = b * b / fm, res.a[1][0] = -a * b / fm, res.a[2][0] = -a * c / fm,
		res.a[1][1] = a * a / fm, res.a[0][1] = -a * b / fm, res.a[2][1] = -b * c / fm;
	}else{
		res.a[0][0] = -fz / fm, res.a[1][0] = -a * b * 2 / fm, res.a[2][0] = -a * c * 2 / fm,
		res.a[1][1] = fz / fm,  res.a[0][1] = -a * b * 2 / fm, res.a[2][1] = -b * c * 2 / fm;
	}
	return res;
}
struct seg{
	struct node{
		matrix v2, v1, a, b;
		bool tag;
	}t[maxn << 2 | 1];
	void upd(int x, matrix a, matrix b, bool tag){
		t[x].a = a * t[x].a;
		t[x].b = t[x].b * b;
		t[x].v1 = a * t[x].v1 * b;
		t[x].v2 = a * t[x].v2 * b;
		if(tag)t[x].tag ^= 1, swap(t[x].v1, t[x].v2);
	}
	void push_down(int x){
		upd(x << 1, t[x].a, t[x].b, t[x].tag);
		upd(x << 1 | 1, t[x].a, t[x].b, t[x].tag);
		t[x].a.I(); t[x].b.I(); t[x].tag = 0;
	}
	void push_up(int x){
		t[x].v1 = t[x << 1].v1 * t[x << 1 | 1].v1;
		t[x].v2 = t[x << 1].v2 * t[x << 1 | 1].v2;
	}
	void build(int x, int l, int r){
		t[x].a.I(); t[x].b.I(); t[x].tag = 0;
		if(l == r){
			int op = read(); double a = read(), b = read(), c = read();
			if(op & 1){
				matrix tmp = trans(-a, -b), re = trans(a, b);
				t[x].v1 = tmp * rota(Pi * c / 1800) * re;
				t[x].v2 = tmp * rota(-Pi * c / 1800) * re;
			}
			else t[x].v1 = t[x].v2 = tr(a, b, c, 1);
			return;
		}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void modify(int x, int l, int r, int L, int R, matrix a, matrix b, bool tag){
		if(L <= l && r <= R)return upd(x, a, b, tag); 
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, a, b, tag);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, a, b, tag);
		push_up(x);
	}
	matrix query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].v1;
		push_down(x); int mid = (l + r) >> 1;
		if(R <= mid)return query(x << 1, l, mid, L, R);
		if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
		return query(x << 1, l, mid, L, R) * query(x << 1 | 1, mid + 1, r, L, R);
	}
}T;
int main(){
	freopen("point.in","r",stdin);
	freopen("point.out","w",stdout);
	n = read(), m = read();
	T.build(1, 1, n);
	for(int i = 1; i <= m; ++i){
		int opt = read();
		if(opt == 1){
			int l = read(), r = read(), u = read(), v = read();
			T.modify(1, 1, n, l, r, trans(-u, -v), trans(u, v), 0);
		}else if(opt == 2){
			int l = read(), r = read(), u = read(), v = read(), w = read();
			matrix tmp = tr(u, v, w, 0);
			T.modify(1, 1, n, l, r, tmp, tmp, 1);
		}else{
			int l = read(), r = read(), x = read(), y = read();
			matrix tmp = T.query(1, 1, n, l, r);
			double px = tmp.a[0][0] * x + tmp.a[1][0] * y + tmp.a[2][0];
			double py = tmp.a[0][1] * x + tmp.a[1][1] * y + tmp.a[2][1];
			printf("%.10lf %.10lf\n", px, py);
		}
	}
	return 0;
}

B. 有限域方阵期望对数转置相关度(transpose)

code
## 题解 - 有限域方阵期望对数转置相关度(transpose)

由于某些历史遗留问题,下面的 $q$ 都指的是体面里的 $p$。

首先考虑存在 $B$ 满足 $AB=A^T$ 当且仅当 $\operatorname{Col} A=\operatorname{Col} A^T$。记 $r=\operatorname{rank} A$ 表示 $A$ 的秩。稍加分析可以得到 $A$ 一定能被唯一地表示为 $A=C^TMC$,其中 $C\in \mathbb F_q^{n \times r}$ 的列构成 $\operatorname{Col} A$ 的一组代表基,$M \in \mathbb F_q^{r\times r}$ 是 $r$ 阶满秩方阵。

考虑对于每个秩为 $r$ 的方阵 $A$,$R_{\log}(A)$ 要么为 $0$,即行空间与列空间不同;要么 $R_{\log}(A)=1+n(n-r)$。于是我们只需要计算有多少个秩为 $r$ 的方阵 $A$ 满足上述条件即可。这个玩意用 q-analogue 搞一下就是 $\frac{[n]_q!}{[r]_q![n-r]_q!}f_q(r)$,其中 $f_q(r)$ 为容易递推的 $r$ 阶满秩矩阵数。于是随便用一种方法任意模数卷积就好。

btw,估计这题也是能用 EI 的科技推出个线性做法的,不过那玩意我不会

C. 灭国(destroy)

肯定要在拓扑排序的过程中进行

我们用一个 \(set\) 维护当前度数为 \(0\) 的点集合 \(A\)

另开一个 \(set\) 维护已经确定需要加上一条指向他的边的点的集合 \(B\)

考虑当前度数为 \(0\) 的点,尽量出大的,那么从小到大加入 \(B\),直到边用完或者剩下一个点

如果当前剩下的一个点的权比 \(B\) 中最大点的权大,那么删去当前点

否则尝试把当前点加入集合 \(B\)

如果当前 \(A\) 集合为空(成功加入 \(B\)) 那么取出 \(B\) 中最大的点删除即可

指向取出点的点实际上取上一个出队的点即可,我赛时想的比较复杂,用的是最后被删掉的标号大于他的点

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 2e5 + 55;

int n, m, k, deg[maxn];
vector<int>g[maxn];
set<int>s, ss;
vector<pii>res;
void print(){
	s.clear();
	for(pii v : res)g[v.first].push_back(v.second);
	for(int i = 1; i <= n; ++i)deg[i] = 0;
	for(int x = 1; x <= n; ++x)
		for(int v : g[x])++deg[v];
	for(int i = 1; i <= n; ++i)if(deg[i] == 0)s.insert(i);
	while(!s.empty()){
		int x = *s.begin(); s.erase(x);
		printf("%d ",x);
		for(int v : g[x]){
			--deg[v];
			if(deg[v] == 0)s.insert(v);
		}
	} printf("\n");
	printf("%d\n",(int)res.size());
	for(pii v : res)printf("%d %d\n",v.first, v.second);
}
void solve1(){
	while(s.size() == 1){
		int x = *s.begin(); s.erase(x);
		for(int v : g[x]){
			--deg[v];
			if(deg[v] == 0)s.insert(v);
		}
	}
	if(!s.empty()){
		int in = *s.begin();
		s.erase(in); int las;
		while(!s.empty()){
			int x = *s.begin(); s.erase(x); if(x > in)las = x;
			for(int v : g[x]){--deg[v]; if(deg[v] == 0)s.insert(v);}
		}
		res.push_back(pii(las, in));
	}
	print();
}
struct seg{
	int t[maxn << 2 | 1];
	void push_down(int x){if(t[x]){t[x << 1] = t[x << 1 | 1] = t[x];t[x] = 0;}}
	void modify(int x, int l, int r, int pos){
		if(r <= pos){t[x] = pos; return;}
		push_down(x); int mid = (l + r) >> 1;
		modify(x << 1, l, mid, pos);
		if(pos > mid)modify(x << 1 | 1, mid + 1, r, pos);
	}
	int query(int x, int l, int r, int pos){
		if(l == r)return t[x];
		push_down(x); int mid = (l + r) >> 1;
		if(pos <= mid)return query(x << 1, l, mid, pos);
		else return query(x << 1 | 1, mid + 1, r, pos);
	}
}T;
int main(){
	freopen("destroy.in","r",stdin);
	freopen("destroy.out","w",stdout);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		++deg[v]; g[u].push_back(v);
	}
	for(int i = 1; i <= n; ++i)if(deg[i] == 0)s.insert(i);
	if(k == 1){
		solve1();
		return 0;
	}
	for(int i = 1; i <= n; ++i)if(deg[i] == 0)s.insert(i);
	int rb = 0, cnt = 0;
	while(cnt < n){
		while(rb < k && (s.size() > 1 || (s.size() == 1 && !ss.empty() && *--ss.end() > *s.begin()))){
			++rb;
			ss.insert(*s.begin());
			s.erase(s.begin());
		}
		++cnt;
		if(!s.empty()){
			int x = *s.begin(); s.erase(x); T.modify(1, 1, n, x);
			for(int v : g[x]){
				--deg[v];
				if(deg[v] == 0)s.insert(v);
			}
		}else{
			int x = *--ss.end(); res.push_back(pii(T.query(1, 1, n, x), x)); ss.erase(x); T.modify(1, 1, n, x);
			for(int v : g[x]){
				--deg[v];
				if(deg[v] == 0)s.insert(v);
			}
		}
	}
	print();
	return 0;
}

2023省选武汉联测8

A. Mix

神必数据结构,不想写

code

B. 梦

code
#include"dream.h"
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 2e5 + 55;

unordered_map<int, int>mp[maxn], id[maxn];
map<int, pii>rem[maxn];
queue<pii>q;
int t1, t2, st[maxn], top, f1[maxn], f2[maxn];
int dream(int ID, int Q, int V, int D, int X, int Y, const std::vector<int>* Edges) {
	if(Edges[X].size() != D && Edges[Y].size() != D)return -1;
	if(Edges[X].size() != D)return Y;
	if(Edges[Y].size() != D)return X;
	if(X == Y)return X; mp[X][Y] = 0;
	while(!q.empty())q.pop();
	for(int i = 0; i < V; ++i)f1[i] = -1;
	for(int i = 0; i < V; ++i)mp[i].clear(), rem[i].clear(), id[i].clear();
	q.push(pii(X, Y)); t1 = t2 = -1;
	while(!q.empty()){
		int x = q.front().first, y = q.front().second;
		q.pop(); int si = Edges[x].size();
		for(int i = 0; i < si; ++i){
			int a = Edges[x][i], b = Edges[y][i];
			if(mp[a].count(b))continue; 
			if(a == f1[x])continue;
			rem[a][b] = pii(x, y); id[a][b] = i;
			if(a == b || Edges[a].size() != Edges[b].size()){
				t1 = a; t2 = b;
				break;
			}else if(mp[x][y] < Q - 1){
				f1[a] = x;
				mp[a][b] = mp[x][y] + 1;
				q.push(pii(a, b));
			}
		}
		if(t1 != -1)break;
	}
	if(t1 == -1){
		while(!q.empty())q.pop();
		for(int i = 0; i < V; ++i)f2[i] = -1;
		for(int i = 0; i < V; ++i)mp[i].clear(), rem[i].clear(), id[i].clear();
		q.push(pii(X, Y));
		while(!q.empty()){
			int x = q.front().first, y = q.front().second;
			q.pop(); int si = Edges[x].size();
			for(int i = 0; i < si; ++i){
				int a = Edges[x][i], b = Edges[y][i];
				if(mp[a].count(b))continue; 
				if(b == f2[y])continue;
				rem[a][b] = pii(x, y); id[a][b] = i;
				if(a == b || Edges[a].size() != Edges[b].size()){
					t1 = a; t2 = b;
					break;
				}else if(mp[x][y] < Q - 1){
					f2[b] = y;
					mp[a][b] = mp[x][y] + 1;
					q.push(pii(a, b));
				}
			}
			if(t1 != -1)break;
		}
		if(t1 == -1)return -1;
	}
	int deg = -1, x = t1, y = t2;
	top = 0;
	while(x != X || y != Y){
		st[++top] = id[x][y];
		pii las = rem[x][y];
		x = las.first; y = las.second;
	}
	for(int i = top; i >= 1; --i)deg = move(st[i]);
	if(deg == Edges[t1].size())return t1;
	else return t2;
}

C. 不平衡度

咕咕咕

code

posted @ 2023-03-08 21:13  Chen_jr  阅读(15)  评论(0编辑  收藏  举报