• JZOJ 2022.07.18【提高组A】模拟


    \(\text{T1}\)

    很容易想到用 \(f_1 f_2 ... f_k\) 来表示第 \(n\)
    发现重点关注指数即可,即我们要递推 \(f_1 ... f_k\) 对应的指数
    递推涉及 \(K\) 项,不难想到矩阵加速递推,
    弄一个 \(K \times K\) 的矩阵维护 \(fi\) 可以表示成 \(f_1 ... f_k\) 及对应指数
    然后就结束了

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <cstring>
    #define IN inline
    typedef long long LL;
    using namespace std;
    
    const int P = 998244353, phi = P - 1;
    int n, K, f[205], b[205];
    
    IN void Add(int &x, int y) {if ((x += y - phi) < 0) x += phi;}
    
    struct Matrix {
    	int n, m, a[205][205];
    	IN Matrix() {n = m = 0, memset(a, 0, sizeof a);}
    	IN Matrix operator * (const Matrix &b) {
    		Matrix c;
    		for(int i = 1; i <= n; i++)
    			for(int k = 1; k <= m; k++)
    			if (a[i][k])
    				for(int j = 1; j <= b.m; j++)
    					Add(c.a[i][j], (LL)a[i][k] * b.a[k][j] % phi);
    		c.n = n, c.m = b.m;
    		return c;
    	}
    }I, A, B, Ans;
    
    IN void Init() {
    	I.n = I.m = K;
    	for(int i = 1; i <= K; i++) I.a[i][i] = 1;
    	A = I, B.n = B.m = K;
    	for(int i = 1; i < K; i++) B.a[i][i + 1] = 1;
    	for(int i = 1; i <= K; i++) B.a[K][i] = b[K - i + 1];
    }
    
    IN Matrix M_pow(Matrix x, int y) {
    	Matrix c = I;
    	for(; y; x = x * x, y >>= 1) if (y & 1) c = c * x;
    	return c;
    }
    IN int fpow(int x, int y) {
    	int res = 1;
    	for(; y; x = (LL)x * x % P, y >>= 1) if (y & 1) res = (LL)res * x % P;
    	return res;
    }
    
    int main() {
    	freopen("seq.in", "r", stdin);
    	freopen("seq.out", "w", stdout);
    	scanf("%d%d", &n, &K);
    	for(int i = 1; i <= K; i++) scanf("%d", &b[i]);
    	for(int i = 1; i <= K; i++) scanf("%d", &f[i]);
    	if (n <= K) {printf("%d\n", f[n]); return 0;}
    	Init(), Ans = A * M_pow(B, n - K);
    	LL ans = 1;
    	for(int i = 1; i <= K; i++) ans = ans * fpow(f[i], Ans.a[K][i]) % P;
    	printf("%lld\n", ans);
    }
    

    \(\text{T2}\)

    最先有想法的题
    一看,点双!圆方树!!
    然后 \(dp\)
    不过发现自己并不擅长在圆方树上的 \(dp\)
    摸索半天。。。
    总结下:
    这题方点用来处理环等信息,起存储作用
    \(dp\) 时为了方便圆点转移,并讨论情况,则需要
    对于圆点,我们枚举其方点再枚举方点的儿子圆点
    好处是提出了一个环,利于环上的转移
    这题就是很好的案例
    因为不知道这样做好转移,折腾半天。。。

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <iostream>
    #include <vector>
    #define IN inline
    using namespace std;
    
    const int N = 2e4 + 5;
    int n, m, tot, h[N], cnt, ans;
    vector <int> g1[N], g2[N];
    
    int dfn[N], low[N], stk[N], tp, dfc, bc[N], st[N], f[N], g[N];
    void Tarjan(int x) {
    	dfn[x] = low[x] = ++dfc, stk[++tp] = x;
    	for(auto v : g1[x])
    		if (!dfn[v]) {
    			Tarjan(v), low[x] = min(low[x], low[v]);
    			if (dfn[x] == low[v]) {
    				++cnt, g2[x].emplace_back(cnt);
    				for(int u = 0; u ^ v; --tp) u = stk[tp], g2[cnt].emplace_back(u);
    				g[cnt] = g2[cnt].size() + 1;
    				if (g[cnt] <= 2) g[x] = 0;
    			}
    		}
    		else low[x] = min(low[x], dfn[v]);
    }
    
    void dfs(int x, int fa) {
    	for(auto v : g2[x]) dfs(v, x), g[x] += g[v];
    	if (x > n && g2[x].size() == 1) g[x] = 0;
    	if (x <= n) {
    		for(auto v : g2[x]) {
    			int sum = 0, t = 1;
    			for(int i = 0; i < g2[v].size(); i++) {
    				f[x] = max(f[x], f[g2[v][i]] + sum + t + g[x] - g[v]);
    				sum += g[g2[v][i]], ++t;
    			}
    			sum = 0, t = 1;
    			for(int i = g2[v].size() - 1; i >= 0; i--) {
    				f[x] = max(f[x], f[g2[v][i]] + sum + t + g[x] - g[v]);
    				sum += g[g2[v][i]], ++t;
    			}
    		}
    	}
    	f[x] = max(f[x], g[x]);
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1, x, y; i <= m; i++) scanf("%d%d", &x, &y), g1[x].emplace_back(y), g1[y].emplace_back(x);
    	cnt = n, Tarjan(1), dfs(1, 0), printf("%d\n", f[1]);
    }
    

    \(\text{T3}\)

    非常显然的莫比乌斯反演,只是需要一条结论

    \[\gcd(F_n,F_m) = F[\gcd(n,m)] \]

    然后就结束了

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <iostream>
    #define IN inline
    using namespace std;
    typedef long long LL;
    
    const int N = 55, M = 1e6, P = 1e9 + 9;
    int a[N], n, pr[M], vis[M + 5], mu[M + 5], cnt, l, r, Mn;
    
    IN void Add(LL &x, LL y) {if ((x += y - P) < 0) x += P;}
    
    void Init() {
    	vis[1] = mu[1] = 1;
    	for(int i = 2; i <= M; i++) {
    		if (!vis[i]) pr[++cnt] = i, mu[i] = -1;
    		for(int j = 1; j <= cnt && pr[j] * i <= M; j++) {
    			vis[pr[j] * i] = 1;
    			if (!(i % pr[j])) break;
    			mu[pr[j] * i] = -mu[i];
    		}
    	}
    	for(int i = 2; i <= M; i++) mu[i] += mu[i - 1];
    }
    
    IN int calc() {
    	int res = 1; r = Mn;
    	for(int i = 1; i <= n; i++)
    		res = (LL)res * (a[i] / l) % P, r = min(r, a[i] / (a[i] / l));
    	return res;
    }
    
    int main() {
    	freopen("point.in", "r", stdin);
    	freopen("point.out", "w", stdout);
    	Init();
    	int T; scanf("%d", &T);
    	for(; T; --T) {
    		scanf("%d", &n);
    		Mn = M;
    		for(int i = 1; i <= n; i++) scanf("%d", &a[i]), Mn = min(Mn, a[i]);
    		LL ans = 0; int s;
    		for(l = 1; l <= Mn; l = r + 1)
    			s = calc(), Add(ans, (LL)(mu[r] - mu[l - 1] + P) * s % P);
    		Mn >>= 1;
    		for(int i = 1; i <= n; i++) a[i] >>= 1;
    		for(l = 1; l <= Mn; l = r + 1)
    			s = calc(), Add(ans, (LL)(mu[r] - mu[l - 1] + P) * s % P);
    		printf("%lld\n", ans);
    	}
    }
    

    \(\text{T4}\)

    经典神题
    需要发现一些性质

    性质 \(1\):两个相交的优美区间相交部分一定是优美区间
    用处是:考虑一个询问区间 \(l,r\),如果暴力枚举 \(R \ge r\)
    找到 \(L <= l\)\([L,R]\) 是优美区间
    那么从小到大枚举 \(R\),第一个成功了的区间一定最短

    性质 \(2\):借以判断优美区间
    很容易想到区间 \([l,r]\) 若满足 \(Max-Min=r-l\) 则此区间为优美区间
    然而我不会用
    另一个角度,优美区间两数差为 \(1\) 的数对数量是 \(r-l\)
    进而可以推出区间中两数差为 \(1\) 的数对数量为 \(n\),且区间长为 \(n+1\),那么这个区间为优美区间
    很好想
    这有什么用处?
    从小到大枚举 \(R\),维护有哪些 \(L\) 使得 \([L,R]\) 为优美区间
    \(R\) 增大,即新增一个数位右端点数,我们可以快速维护 \(L\)
    即考虑包含 \(a[R]-1,a[R]+1\) 的区间,这些区间两数差为 \(1\) 的数对数量均可加 \(1\)
    并且只需用这个条件就可判断优美区间了,线段树支持区间加
    最短的话线段树上二分找到最右的 \(L\) 使得 \([L,R]\) 为优美区间即可
    而发现一个询问可能要查询很多次而没有结果
    优化是给询问排序,用堆维护当前 \(r \le R\) 的询问中 \(L\) 从大到小的顺序
    当前 \(R\) 下,若这个询问失败,后面的询问必然失败,可以终止查询,以少做无用功

    问号:这是原题 \(\text{P4747 [CERC2017]Intrinsic Interval}\)

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <queue>
    #include <iostream>
    #include <algorithm>
    #define IN inline
    using namespace std;
    
    const int N = 1e5 + 5;
    int n, m, a[N], R, pos[N];
    
    struct Que {
    	int l, r, id;
    	IN bool operator < (const Que &a) const {return r < a.r;}
    }b[N];
    struct node {
    	int l, id;
    	IN bool operator < (const node &a) const {return l < a.l;}
    };
    priority_queue <node> Q;
    struct Rev {int l, r;}ans[N];
    
    #define ls (p << 1)
    #define rs (ls | 1)
    int mx[N << 2], tag[N << 2];
    
    IN void pushup(int p) {mx[p] = max(mx[ls], mx[rs]);}
    IN void pushdown(int p) {
    	if (!tag[p]) return;
    	mx[ls] += tag[p], mx[rs] += tag[p], tag[ls] += tag[p], tag[rs] += tag[p];
    	tag[p] = 0;
    }
    
    void Modify(int p, int l, int r, int x, int y, int v) {
    	if (x <= l && r <= y) return tag[p] += v, mx[p] += v, void();
    	pushdown(p);
    	int mid = l + r >> 1;
    	if (x <= mid) Modify(ls, l, mid, x, y, v);
    	if (y > mid) Modify(rs, mid + 1, r, x, y, v);
    	pushup(p);
    }
    
    int Query(int p, int l, int r, int x) {
    	if (mx[p] < R) return 0;
    	if (l == r) return l;
    	pushdown(p);
    	int mid = l + r >> 1, res = 0;
    	if (x > mid && mx[rs] >= R) res = Query(rs, mid + 1, r, x);
    	if (res) return res;
    	if (mx[ls] >= R) res = Query(ls, l, mid, x);
    	return res;
    }
    
    int main() {
    	freopen("sequence.in", "r", stdin);
    	freopen("sequence.out", "w", stdout);
    	scanf("%d", &n);
    	for(int i = 1; i <= n; i++) scanf("%d", &a[i]), pos[a[i]] = i;
    	scanf("%d", &m);
    	for(int i = 1; i <= m; i++) scanf("%d%d", &b[i].l, &b[i].r), b[i].id = i;
    	sort(b + 1, b + m + 1);
    	for(int i = 1, j = 1; i <= n; i++) {
    		R = i, Modify(1, 1, n, i, i, i);
    		if (a[i] > 1 && pos[a[i] - 1] <= i) Modify(1, 1, n, 1, pos[a[i] - 1], 1);
    		if (a[i] < n && pos[a[i] + 1] <= i) Modify(1, 1, n, 1, pos[a[i] + 1], 1);
    		while (j <= m && b[j].r <= i) Q.push(node{b[j].l, b[j].id}), ++j;
    		while (!Q.empty()) {
    			node now = Q.top();
    			int gd = Query(1, 1, n, now.l);
    			if (gd) ans[now.id] = Rev{gd, i}, Q.pop();
    			else break;
    		}
    	}
    	for(int i = 1; i <= m; i++) printf("%d %d\n", ans[i].l, ans[i].r);
    }
    
  • 相关阅读:
    分布式系统阅读笔记(六)-----间接通信
    分布式系统阅读笔记(六)-----间接通信
    分布式系统阅读笔记(六)-----间接通信
    分布式系统阅读笔记(七)-----操作系统的支持
    分布式系统阅读笔记(七)-----操作系统的支持
    分布式系统阅读笔记(七)-----操作系统的支持
    分布式系统阅读笔记(八)-----分布式对象和组件
    sizeof()
    void *
    strcpy函数的实现
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/16492040.html
Copyright © 2020-2023  润新知