闲话 22.10.13

闲话

压位trie怎么实现?哪天写一个

好于是今天卡了一天的常数
然后lyin十分钟给切了

没什么要写的诶今天
哦对了今天中午换起床歌了
瑞苹 不如新宝岛

谁有什么很诡异的题来作社论题材啊

[最近突然在唱moon halo 但是我想推的是崩三 所以不放歌词了]

关于CF896E

接着炒冷饭

维护一个\(n(n\leqslant 100000)\)个元素序列\(a_1,a_2,\dots,a_n\),有\(m(m\leqslant 100000)\)次操作,分为如下两种。

  1. 给定\(l,r,x\),将\(a_l,a_{l+1},\dots,a_r\)中所有大于\(x\)的元素减去\(x\)
  2. 给定\(l,r,x\),询问\(a_l,a_{l+1},\dots,a_r\)中,有多少个元素恰好等于\(x\)

\(1\leqslant l\leqslant r\leqslant n,1\leqslant a_i,x\leqslant 100000\)


最近有人被 P4117 五彩斑斓的世界 这题恶心到了,我觉得有必要普及一下这题恶心卡常的原因。

最开始 P4117 是一道小清新分块题,数据范围啥的和 CF896E 都是差不多的 1e5,就是没有作为比赛题,稍微卡了卡限制。
然后 WC2019 Day4,mcfx 上去讲了一些卡常技巧。其中
拿 P4117 举 了 一 个 例 子。
具体是什么例子?\(O(nm \textbf{/ 8})\) 如何过标算。

暴力 by mcfx

使用指令集的暴力程序(总用时:2889ms / 内存:1068KB)

image

std by lxl

lxl的标程(总用时: 4312ms / 内存: 81948KB)

image

lxl寄了。然后lxl急了。

mcfx 在课件里打了这么一段话:

可惜的是,虽然除了常数,但是复杂度还是O(nm)。这导致如果n,m扩倍的话时间增长和标算差得远。比如n,m=3e5的时候指令集暴力就会原地爆炸,会比n,m=1e5慢整整9倍。。。

然后 P4117 的数据范围就变成了 1e6 和 5e5。

然后你该怎么写呢?“操作按块离线优化空间,大力卡常,数组压到最小,x+lazy 大于 \(10^5\) 的询问不处理,不在范围内的跳过。

最重要的是,所有的函数前面,加上inline。”


然后讲一下CF896E该怎么写。

这题的关键部分是两个东西:第二分块和均摊证明。

第二分块是什么?用于维护区间将所有 \(x\) 修改成 \(y\) 的操作。
我们首先进行一个分块,每块维护一个开在值域上的并查集,把所有值为 \(x\) 的节点都放在一个并查集里面。对于修改,整块直接把 \(y\) 的并查集连在 \(y\) 上,散块就暴力维护。并查集记录一下大小,修改的时候按大小维护对答案的贡献。

如果你不太会实现,可以先做一下板子。

【模板】第二分块

给定长度为 \(n\) 的序列 \(a\),你需要支持如下两种操作:

1.1 l r x y,表示将 \([l,r]\) 内所有值为 \(x\) 的元素的值改为 \(y\)

2.2 l r,表示输出 \(\large\prod\limits_{i = l}^{r} C_{\sum_{j = l}^{i}a_j}^{a_i}\ \bmod 998244353\) 的值。

\(1 \le n,q,a_i \le 10^5\)。有 \(1 \le l,r\le n;1 \le x,y\le 10^5\)。任意时刻 \(\sum a\) 不会超过 \(10^7\)

化完柿子照着上面的方式实现就行了。

code
#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;
namespace Fread  { const int SIZE = (1 << 18); char buf[SIZE], *p1 = buf, *p2 = buf; inline char getchar() {return (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, SIZE, stdin), p1 == p2) ? EOF : *p1++);} }
namespace Fwrite { const int SIZE = (1 << 18); char buf[SIZE], *S = buf, *T = buf+SIZE; inline void flush(){ fwrite(buf, 1, S-buf, stdout), S = buf; }  struct NTR{ ~NTR() { flush(); } }ztr;inline void putchar(char c){ *S++ = c; if(S == T) flush(); } }
#ifdef ONLINE_JUDGE
    #define getchar Fread::getchar
    #define putchar Fwrite::putchar
#endif
namespace Fastio{
    struct Reader{ template <typename T> Reader & operator >> (T & x) {char c = getchar(); bool f = false;while (c < '0' or c > '9') { if (c == '-') f = true;c = getchar();} x = 0;while(c >= '0' and c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} if (f) x = -x;return *this;}Reader&operator>>(char&c){ c=getchar();while(c=='\n'||c==' '||c=='\r')c=getchar();return *this;}Reader&operator>>(char*str){ int len=0;char c=getchar(); while(c=='\n'||c==' '||c=='\r')c=getchar(); while(c!='\n'&&c!=' '&&c!='\r')str[len++]=c,c=getchar(); str[len]='\0'; return *this;}Reader(){}}cin;
    struct Writer{ template <typename T> Writer & operator << (T   x) {if(x == 0) return putchar('0'), *this;if(x < 0) putchar('-'), x = -x;static int sta[45], top = 0; while (x)  sta[++top] = x %10, x /= 10; while (top)  putchar(sta[top] + '0'), --top; return *this;} Writer&operator<<(char c){putchar(c);return*this;}Writer&operator<<(const char*str){int cur=0;while(str[cur])putchar(str[cur++]);return *this;}Writer(){}}cout;
}   const char * endl = "\n"; const char * space = " ";
#define cin  Fastio :: cin
#define cout Fastio :: cout
#define mod 998244353
#define RGE 10000005
#define N 100005
#define siz 390
#define rep(i,a,b) for ( register int (i) = (a); (i) <= (b); ++(i) )
#define pre(i,a,b) for ( register int (i) = (a); (i) >= (b); --(i) )
int n, q, a[N];
int fac[RGE], inv[RGE];
int bl[N], sz[N / siz + 5], st[N / siz + 5], ed[N / siz + 5], sum[N / siz + 5], prod[N / siz + 5], pw_fac[N][siz + 2], pw_inv[N][siz + 2];

inline int qp(int a, int b) {
	int ret = 1;
	while (b) {
		if (b & 1) ret = 1ll * ret * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	} return ret;
}

struct block_id {
	int viv, cnt;
}g[N / siz + 5][N];

int fa[N], v[N];
inline int get(int x) {
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}
inline void init(int id) {
	prod[id] = 1, sum[id] = 0;
	rep(i, st[id], ed[id]) {
		if (g[id][a[i]].viv) fa[i] = g[id][a[i]].viv, g[id][a[i]].cnt++;
		else fa[i] = g[id][a[i]].viv = i, v[i] = a[i], g[id][a[i]].cnt = 1;
		sum[id] += a[i];
		prod[id] = 1ll * prod[id] * inv[a[i]] % mod;
	}
}
inline void ps_d(int id) {
	rep(i, st[id], ed[id]) {
		a[i] = v[get(i)];
		g[id][a[i]].viv = g[id][a[i]].cnt = 0;
	} rep(i, st[id], ed[id]) fa[i] = 0;
}

inline void deal_side(int id, int l, int r, int from, int to) {
	ps_d(id);
	rep(i, l, r) if(a[i] == from) a[i] = to;
	init(id);
}
inline void deal_whole(int id, int from, int to) {
	sum[id] += (to - from) * g[id][from].cnt;
	prod[id] = 1ll * prod[id] * pw_inv[to][g[id][from].cnt] % mod * pw_fac[from][g[id][from].cnt] % mod;
	g[id][to].cnt += g[id][from].cnt;
	if (g[id][to].viv == 0) g[id][to].viv = g[id][from].viv, v[g[id][to].viv] = to;
	else fa[g[id][from].viv] = g[id][to].viv;
	g[id][from].cnt = g[id][from].viv = 0;
}

inline void change(int l, int r, int from, int to) {
	if (bl[l] == bl[r]) {
		deal_side(bl[l], l, r, from, to);
	} else {
		int lf, rt;
		if (st[bl[l]] != l) {
			deal_side(bl[l], l, ed[bl[l]], from, to);
			lf = bl[l] + 1;
		} else {
			lf = bl[l];
		} if (ed[bl[r]] != r) {
			deal_side(bl[r], st[bl[r]], r, from, to);
			rt = bl[r] - 1;
		} else {
			rt = bl[r];
		}
		rep(i, lf, rt) deal_whole(i, from, to);
	}
}

inline int qry(int l, int r) {
	int tmpsum = 0, tmpprod = 1, tmp;
	if (bl[l] == bl[r]) {
		rep(i,l,r) tmp = v[get(i)], tmpsum += tmp, tmpprod = 1ll * tmpprod * inv[tmp] % mod;
	} else {
		int lf, rt;
		if (st[bl[l]] != l) {
			rep(i,l,ed[bl[l]]) tmp = v[get(i)], tmpsum += tmp, tmpprod = 1ll * tmpprod * inv[tmp] % mod;
			lf = bl[l] + 1;
		} else {
			lf = bl[l];
		} if (ed[bl[r]] != r) {
			rep(i,st[bl[r]],r) tmp = v[get(i)], tmpsum += tmp, tmpprod = 1ll * tmpprod * inv[tmp] % mod;
			rt = bl[r] - 1;
		} else {
			rt = bl[r];
		} rep(i, lf, rt) tmpsum += sum[i], tmpprod = 1ll * tmpprod * prod[i] % mod;
	} return 1ll * fac[tmpsum] * tmpprod % mod;
}

signed main() {
	register int opr, l, r, x, y;
	cin >> n >> q;
	
	fac[1] = inv[1] = 1;
	rep(i,2,RGE-5) fac[i] = 1ll * i * fac[i-1] % mod, inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
	rep(i,2,RGE-5) inv[i] = 1ll * inv[i] * inv[i-1] % mod;
	rep(i,1,n) bl[i] = (i-1) / siz + 1;
	rep(i,1,bl[n] - 1) sz[i] = siz;
	sz[bl[n]] = ((n % siz == 0) ? siz : n % siz);
	st[1] = 1;
	rep(i,2,bl[n]) st[i] = st[i-1] + sz[i-1]; 
	rep(i,1,bl[n]-1) ed[i] = st[i+1] - 1;
	ed[bl[n]] = n;
	rep(i, 1, N-5) {
		pw_fac[i][0] = pw_inv[i][0] = 1;
		rep(j, 1, siz + 1) {
			pw_fac[i][j] = 1ll * pw_fac[i][j-1] * fac[i] % mod;
			pw_inv[i][j] = 1ll * pw_inv[i][j-1] * inv[i] % mod;
		} 
	} 

	rep(i,1,n) cin >> a[i];
	rep(i,1,bl[n]) init(i);
	rep(i,1,q) {
		cin >> opr >> l >> r;
		if (opr == 1) {
			cin >> x >> y;
			if (x == y) continue;
			change(l, r, x, y);
		} else {
			cout << qry(l, r) << endl;
		} 
	}
}

然后是第二个部分:均摊证明。
你发现原题的第一个操作有点不一样。然而,对于每块的修改都是 \(O(n)\) 的,这样能导出 \(O(n \sqrt n)\) 的整块部分时间复杂度。又因为散块在每次询问中都只会被修改 \(O(1)\) 块,因此能得到总的时间复杂度 \(O(n \sqrt n)\)

证明

考虑均摊证明方法。这里认为值域为 \(O(n)\)
我们只需证明对于每个整块的修改都是 \(O(n)\) 的。

考虑一次修改的意义。一次对某整块中大于 \(x\) 元素的修改使得该块内元素最大值和小于等于 \(x\)。容易发现有意义的修改最多执行 \(O(值域)\) 次,值域内每个元素最多会被包含在一次修改中。

因此每块作为整块的复杂度都是 \(O(n)\) 的。

杂题

[WC2011]最大XOR和路径

题面长,不放。

假如说一条路径被走了两次,那这条路径是不会被算贡献的。因此最终的答案一定是一堆环和一条路径拼起来的。

我们首先找到所有的环。将环上路径的xor和扔进一个线性基里面。
然后我们随便找一条 \(1\to n\) 的路径,查询线性基和这条路径xor和的最大值。

容易发现这样能够枚举所有情况,因为最大路径一定能由任意一个路径和最大路径组成的环xor那条路径得到。

code
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 1e6 + 10, mod = 1e9 + 7; typedef long long ll;
int n, m, t1, t2;
ll t3;

#define Aster(s) for (int i = head[s]; i; i = e[i].next)
#define v e[i].to
#define w e[i].wei
int head[N], mlc;
struct ep {
    int to, next;
    ll wei;
} e[N << 1];
void adde(int f, int t, ll wei) {
    e[++mlc] = { t, head[f], wei };
    head[f] = mlc;
    e[++mlc] = { f, head[t], wei };
    head[t] = mlc;
}

struct LB {
    ll a[65];
    void insert(ll k) {
        pre(i,60,0) {
            if (((k >> i) & 1) == 0) continue;
            if (a[i] == 0) { a[i] = k; break; }
            else k ^= a[i];
        }
    }
    ll query(ll val) {
        pre(i,60,0) if ((val ^ a[i]) > val) val ^= a[i];
        return val;
    }
} L;

ll tmp[N];
bool vis[N];
void dfs(int u, ll res) {
    tmp[u] = res; vis[u] = 1;
    Aster(u) {
        if (!vis[v]) dfs(v, res ^ w);
        else L.insert(res ^ tmp[v] ^ w);
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    rep(i,1,m) cin >> t1 >> t2 >> t3, adde(t1, t2, t3);
    dfs(1, 0);
    cout << L.query(tmp[n]);
}
posted @ 2022-10-13 17:13  joke3579  阅读(82)  评论(3编辑  收藏  举报