2023武汉联训

2023省选武汉联测1

算是 Day1?

大佬好多,我好菜

A. 递归函数

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

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

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

然后不太会了

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

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

枚举 pa 每次有 1 的贡献

转移只有 kpa 特殊(有多的一个贡献)

于是求出 pa 的矩阵然后快速幂,处理一下边界即可

对于 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>=m 的,考虑其每个结点会贡献 fail 树上到根的路径中经过的其他串的结尾,

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

对于 len<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 只会在进位时贡献一次)

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

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

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

再维护之后的连续的 1 的个数

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

于是每次就可以在 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 分块

块内询问直接山海经

考虑块间

ai 表示前一个块后 leni+1 个数的后缀和

bi 表示后一个块前 i 个数的前缀和

那么限制变成 i<=j bi+aj 的最大值

然后怎么维护一下。。。

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[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 ij 无边 jk 无边,则 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. 线性代数

解释一下系数2k1,就是看有奇数/偶数个主元在这一位为 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 @   Chen_jr  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示