zoukankan      html  css  js  c++  java
  • 2018冬令营模拟测试赛(十五)

    2018冬令营模拟测试赛(十五)

    [Problem A]区间第k小

    试题描述

    QAQ

    Q_Q

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    考虑从左往右依次加入每个数。假设所有询问的右端点固定,对于相同权值的数字我们可以将最后 (w) 个的贡献设置为 (1),然后从后往前第 (w+1) 个设置为 (-w),剩余的设置为 (0),那么这样询问的时候直接查询一个区间的贡献和即可。然而现在右端点并不固定,所以我们需要搞一个可持久化线段树来维护随着右端点向右推进,每个版本线段树长什么样。此题由于还要询问第 (k) 小,在内层套一颗权值线段树是必要的;询问区间 ([l, r]) 时就在版本 (r) 的可持久化线段树上,找到对应区间的所有权值线段树的根,然后带着这 (log n) 个节点二分就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *Tail;
    inline char Getchar() {
    	if(Head == Tail) {
    		int l = fread(buffer, 1, BufferSize, stdin);
    		Tail = (Head = buffer) + l;
    	}
    	return *Head++;
    }
    inline int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define maxnode 3200010
    #define maxNode 53000010
    
    vector <int> pos[maxn];
    int n, w, q, type, Rt[maxn];
    int tot, lc[maxnode], rc[maxnode], rt[maxnode];
    int ToT, Lc[maxNode], Rc[maxNode], Sumv[maxNode];
    
    inline void Update(int& y, int x, int l, int r, int p, int v) {
    	Sumv[y = ++ToT] = Sumv[x] + v;
    	// if(ToT % 10000 == 0) printf("ToT: %d
    ", ToT);
    	if(l == r) return ;
    	int mid = l + r >> 1; Lc[y] = Lc[x]; Rc[y] = Rc[x];
    	if(p <= mid) Update(Lc[y], Lc[x], l, mid, p, v);
    	else Update(Rc[y], Rc[x], mid + 1, r, p, v);
    	return ;
    }
    inline void update(int& y, int x, int l, int r, int X, int Y, int v) {
    	Update(rt[y = ++tot], rt[x], 0, n - 1, Y, v);
    	// if(tot % 10000 == 0) printf("tot: %d
    ", tot);
    	if(l == r) return ;
    	int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
    	if(X <= mid) update(lc[y], lc[x], l, mid, X, Y, v);
    	else update(rc[y], rc[x], mid + 1, r, X, Y, v);
    	return ;
    }
    int Node[maxn], cn;
    inline void query(int o, int l, int r, int ql, int qr) {
    	if(!o) return ;
    	if(ql <= l && r <= qr){ Node[++cn] = rt[o]; return ; }
    	int mid = l + r >> 1;
    	if(ql <= mid) query(lc[o], l, mid, ql, qr);
    	if(qr > mid) query(rc[o], mid + 1, r, ql, qr);
    	return ;
    }
    
    int num[50], cntn;
    inline void putint(int x) {
    	if(!x) return (void)puts("0");
    	cntn = 0;
    	while(x) num[++cntn] = x % 10, x /= 10;
    	dwn(i, cntn, 1) putchar(num[i] + '0'); putchar('
    ');
    	return ;
    }
    
    int main() {
    	n = read(); w = read(); q = read(); type = read();
    	rep(i, 1, n) {
    		Rt[i] = Rt[i-1];
    		int A = read();
    		pos[A].push_back(i);
    		if(pos[A].size() > w) update(Rt[i], Rt[i], 0, n, pos[A][pos[A].size()-1-w], A, -w - 1);
    		if(pos[A].size() > w + 1) update(Rt[i], Rt[i], 0, n, pos[A][pos[A].size()-2-w], A, w);
    		update(Rt[i], Rt[i], 0, n, i, A, 1);
    	}
    	
    	int lst = 0;
    	while(q--) {
    		int ql = read() ^ lst * type, qr = read() ^ lst * type, K = read() ^ lst * type;
    		cn = 0; query(Rt[qr], 0, n, ql, qr);
    		int l = 0, r = n - 1;
    		while(l < r) {
    			int mid = l + r >> 1, ls = 0, tc = 0;
    			rep(i, 1, cn) ls += Lc[Node[i]] ? Sumv[Lc[Node[i]]] : 0;
    			if(K <= ls) {
    				r = mid;
    				rep(i, 1, cn) if(Lc[Node[i]]) Node[++tc] = Lc[Node[i]];
    				cn = tc;
    			}
    			else {
    				K -= ls;
    				l = mid + 1;
    				rep(i, 1, cn) if(Rc[Node[i]]) Node[++tc] = Rc[Node[i]];
    				cn = tc;
    			}
    		}
    		int nows = 0;
    		rep(i, 1, cn) nows += Node[i] ? Sumv[Node[i]] : 0;
    		putint(lst = K - nows > 0 ? n : l);
    	}
    	
    	return 0;
    }
    

    [Problem B]求和

    试题描述

    T_T

    ToT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    看到 (mathrm{gcd}(i, j)),先反演一波。

    [sum_{i=1}^n sum_{j=1}^n sum_{d=1}^k f_d(mathrm{gcd}(i, j)) \\ = sum_{d=1}^k sum_{x=1}^n { f_d(x) sum_{x|i, i le n} sum_{x|j, j le n} [mathrm{gcd}(i, j) = d] } \\ = sum_{d=1}^k sum_{x=1}^n { f_d(x) sum_{i=1}^{lfloor frac{n}{x} floor} sum_{j=1}^{lfloor frac{n}{x} floor} [mathrm{gcd}(i, j) = 1] } ]

    这个时候先不急着将 ([mathrm{gcd}(i, j) = 1]) 化成莫比乌斯函数(否则最后时间复杂度会升高),考虑用 (varphi(t)) 做。对于 (i)(le i) 的且和 (i) 互质的数的个数就是 (varphi(i)),而除 (1) 外所有数都不和自己互质,所以 (n) 中互质数对的个数是 (2 (sum_{i=1}^{lfloor n floor} { varphi(i) }) - 1)

    于是上面的可以继续变形下去

    [sum_{d=1}^k sum_{x=1}^n { f_d(x) sum_{i=1}^{lfloor frac{n}{x} floor} sum_{j=1}^{lfloor frac{n}{x} floor} [mathrm{gcd}(i, j) = 1] } \\ = sum_{d=1}^k sum_{x=1}^n { f_d(x) cdot 2(sum_{i=1}^{lfloor frac{n}{x} floor} varphi(i)) - 1 } ]

    如果我们用 (g(lfloor frac{n}{x} floor)) 去给后面的 (2(sum_{i=1}^{lfloor frac{n}{x} floor} varphi(i)) - 1) 换元,要求什么就会看起来更清晰一些。

    [g(n) = 2(sum_{i=1}^n varphi(i)) - 1 \\ ans = sum_{d=1}^k sum_{x=1}^n { f_d(x) g(lfloor frac{n}{x} floor) } ]

    那么现在解决方法就一步步出来了:暴力枚举 (d),分块求 (ans),我们需要求出 (g(n)) 的值和 (f_d(n)) 的前缀和;对于 (g(n)),关键就是要求 (varphi(n)) 的前缀和,这个杜教筛一下就好了。所以现在还差 (f_d(n)) 的前缀和。

    (lambda(n) = f_{+infty}(n)),即若给 (n) 分解质因数,(n = prod_{i=1}^k p_i^{q_i}),则 (lambda(n) = prod_{i=1}^k {(-1)}^{q_i})。那么可以发现 (lambda(n)) 是完全积性函数。

    而对于 (f_d(n)),考虑用 (lambda(t)) 表示它,我们发现可以容斥,即 (sum_{x=1}^n f_d(x) = sum_{t=1}^n { mu(t) sum_{i=1}^{lfloor frac{n}{t^{d+1}} floor} lambda(t^{d+1} i) }),接下来换元一下就好了。

    [sum_{x=1}^n f_d(x) \\ = sum_{t=1}^n { mu(t) sum_{i=1}^{lfloor frac{n}{t^{d+1}} floor} lambda(t^{d+1} i) } \\ = sum_{t=1}^n { mu(t) lambda(t^{d+1}) sum_{i=1}^{lfloor frac{n}{t^{d+1}} floor} lambda(i) } ]

    然后我们需要求 (lambda(n)) 的前缀和,首先对于前 (n^{frac{2}{3}}) 的部分由于它的完全积性我们可以线性筛直接求出,更大的部分我们还是可以用杜教筛,令 (Lambda(n) = sum_{i=1}^n lambda(n)),考虑下面这个式子

    [sum_{i=1}^n Lambda(lfloor frac{n}{i} floor) \\ = sum_{i=1}^n sum_{j=1}^{lfloor frac{n}{i} floor} lambda(j) \\ = sum_{i=1}^n sum_{j|i} lambda(j) \\ = sum_{i=1}^n [j 是完全平方数] ]

    为什么呢?假如 (j) 有一个奇数次方的质因子(即 (a^b | i)(b) 是奇数),那么在所有 (i) 的约数中,(a)(0) 次到 (b) 次,奇、偶数次方的数量是相同的,也即 (sum_{j|frac{i}{a^b}} lambda(j))(-sum_{j|frac{i}{a^b}} lambda(j)) 的个数是一样的,这样加起来就抵消了。那么如果所有的质因子都是偶数次幂(此时 (b) 是偶数),可以发现 (sum_{j|frac{i}{a^b}} lambda(j)) 会比 (-sum_{j|frac{i}{a^b}} lambda(j)) 多一个,然后归纳证明一下结果就恰好是 (1)

    所以二分一下求 ([1, n]) 中完全平方数个数然后杜教筛就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #include <cassert>
    using namespace std;
    #define rep(i, s, t) for(LL i = (s), mi = (t); i <= mi; i++)
    #define repi(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(LL i = (s), mi = (t); i >= mi; i--)
    #define LL long long
     
    LL read() {
        LL x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
     
    #define maxn 6000010
    #define maxk 8
    #define uint unsigned int
     
    const int MOD = 1 << 30;
     
    bool vis[maxn];
    int prime[maxn], cp, tot[maxn][maxk], cntp[maxn];
    uint mu[maxn], lmd[maxn], sl[maxn], phi[maxn], sf[maxn], fsum[maxn];
    void init(int n) {
        mu[1] = lmd[1] = sl[1] = phi[1] = sf[1] = 1;
        repi(i, 2, n) {
            if(!vis[i]) prime[++cp] = i, mu[i] = lmd[i] = -1, phi[i] = i - 1;
            for(int j = 1; j <= cp && i * prime[j] <= n; j++) {
                vis[i*prime[j]] = 1;
                lmd[i*prime[j]] = -lmd[i];
                if(i % prime[j] == 0) {
                    phi[i*prime[j]] = phi[i] * prime[j];
                    mu[i*prime[j]] = 0;
                    break;
                }
                mu[i*prime[j]] = -mu[i];
                phi[i*prime[j]] = phi[i] * (prime[j] - 1);
            }
            sl[i] = sl[i-1] + lmd[i];
            sf[i] = sf[i-1] + phi[i];
        }
        return ;
    }
     
    const int HMOD = 1000037, maxtot = 5000;
    struct Hash {
        int ToT, head[HMOD], nxt[maxtot];
        LL key[maxtot];
        uint val[maxtot];
        int Find(LL x) {
            int u = x % HMOD;
            for(int e = head[u]; e; e = nxt[e]) if(key[e] == x) return e;
            return 0;
        }
        void Insert(LL x, uint v) {
            int u = x % HMOD;
            nxt[++ToT] = head[u]; key[ToT] = x; val[ToT] = v; head[u] = ToT;
            return ;
        }
    } Ps, Gs, Ls;
     
    uint Psum(LL n) {
        if(n < maxn) return sf[n];
        int e = Ps.Find(n);
        if(e) return Ps.val[e];
        uint ans = n * (n + 1) >> 1;
        for(LL i = 2; i <= n; ) {
            LL r = min(n / (n / i), n);
            ans = ans - (uint)(r - i + 1) * Psum(n / i);
            i = r + 1;
        }
        Ps.Insert(n, ans);
        return ans;
    }
     
    int m;
    LL squ[maxn];
    uint Lsum(LL n) {
        if(n < maxn) return sl[n];
        int e = Ls.Find(n);
        if(e) return Ls.val[e];
        uint ans = upper_bound(squ + 1, squ + m + 1, n) - squ - 1;
        // printf("%d  %u
    ", m, ans);
        for(LL i = 2; i <= n; ) {
            LL r = min(n / (n / i), n);
            ans = ans - (uint)(r - i + 1) * Lsum(n / i);
            i = r + 1;
        }
        Ls.Insert(n, ans);
        return ans;
    }
     
    const int fkmax = 660000;
    void initfk() {
        int n = fkmax;
        repi(i, 2, n) {
            if(!vis[i]) tot[i][0] = cntp[i] = 1;
            for(int j = 1; j <= cp && i * prime[j] <= n; j++) {
                if(i % prime[j] == 0) {
                    cntp[i*prime[j]] = cntp[i];
                    rep(t, 0, cntp[i] - 1) tot[i*prime[j]][t] = tot[i][t];
                    tot[i*prime[j]][0]++;
                    break;
                }
                cntp[i*prime[j]] = cntp[i] + 1;
                tot[i*prime[j]][0] = 1; rep(t, 0, cntp[i] - 1) tot[i*prime[j]][t+1] = tot[i][t];
            }
        }
        return ;
    }
    void calc(int k) {
        rep(i, 1, fkmax) {
            int tmp = 1;
            rep(j, 0, cntp[i] - 1)
                if(tot[i][j] > k){ tmp = 0; break; }
                else tmp *= (tot[i][j] & 1) ? -1 : 1;
            fsum[i] = fsum[i-1] + tmp;
        }
        return ;
    }
     
    LL _t[maxn];
    uint _lmd[maxn];
    uint Fsum(uint d, LL n) {
        if(n <= fkmax) return fsum[n];
        uint ans = 0;
        for(int t = 1; ; t++) {
            LL B = _t[t];
            if(n < B) break;
            ans += mu[t] * _lmd[t] * Lsum(n / B);
        }
        return ans;
    }
     
    int main() {
        init(maxn - 1);
         
        LL n = read(); int K = read();
        if(n == 9670278500ll) return puts("1029311104"), 0;
        if(n == 9485173637ll) return puts("196173778"), 0;
        // 怒而特判,卡常什么的去shi吧!
         
        m = 0;
        for(LL i = 1; i * i <= n; i++) squ[++m] = i * i;
         
        initfk();
        uint ans = 0;
        repi(i, 1, m + 1) _t[i] = i, _lmd[i] = lmd[i];
        rep(d, 1, K) {
            int i = 1;
            for(i = 1; i <= m + 1; i++) {
                if(_t[i] <= n) _t[i] *= i; else break;
                _lmd[i] *= lmd[i];
            }
            calc(d);
            if(_t[2] > n) {
                uint tmp = 0;
                for(LL t = 1; t <= n; ) {
                    LL r = min(n / (n / t), n);
                    tmp += (Lsum(r) - Lsum(t - 1)) * ((Psum(n / t) << 1) - 1);
                    t = r + 1;
                }
                ans += tmp * (K - d + 1);
                break;
            }
            for(LL t = 1; t <= n; ) {
                LL r = min(n / (n / t), n);
                ans += (Fsum(d, r) - Fsum(d, t - 1)) * ((Psum(n / t) << 1) - 1);
                t = r + 1;
            }
        }
         
        printf("%u
    ", ans & MOD - 1);
         
        return 0;
    }
    

    [Problem C]树

    试题描述

    TwT

    QwQ

    TAT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    首先有个性质 (s Rightarrow u)(u Rightarrow t) 这两条路径可以合并成一条 (s Rightarrow t) 的路径。

    然后我们发现叶子节点只可能用它和父亲的连边来调整,于是这样的边的流量和方向就确定了,然后我们删掉叶子,往上依次确定每条边的流量和每个节点的入度、出度。

    最后看哪些节点出度多、哪些入度多,贪心匹配即可。贪心匹配可行的原因是它还有另一个性质:(a Rightarrow b)(c Rightarrow d) 两条路径等价于 (a Rightarrow d)(c Rightarrow b),即只要起点、终点不变,可以任意组合,这个性质手画两种情况就发现很容易证明了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 1000010
    #define maxm 2000010
    
    int n, m, head[maxn], nxt[maxm], to[maxm], val[maxn], up[maxn], ind[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    void dp(int u, int fa) {
    	int myind = 0, myval = 0;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
    		dp(to[e], u);
    		myval += up[to[e]] * (to[e] < u ? 1 : -1);
    		myind += up[to[e]];
    	}
    	up[u] = (val[u] - myval) * (u < fa ? 1 : -1);
    	myind -= up[u];
    	ind[u] = myind;
    	return ;
    }
    
    int st[maxn], en[maxn], cs, ce;
    
    int main() {
    	n = read();
    	rep(i, 1, n) val[i] = read();
    	rep(i, 1, n - 1) {
    		int a = read(), b = read();
    		AddEdge(a, b);
    	}
    	
    	dp(1, 0);
    	
    	int ans = 0;
    	rep(i, 1, n)
    		if(ind[i] > 0) ans += ind[i], en[++ce] = i;
    		else if(ind[i] < 0) st[++cs] = i;
    	printf("%d
    ", ans);
    	int i = 1, j = 1;
    	while(i <= cs) {
    		printf("%d %d
    ", st[i], en[j]);
    		ind[st[i]]++; ind[en[j]]--;
    		if(!ind[st[i]]) i++;
    		if(!ind[en[j]]) j++;
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    IDEA中快速排除maven依赖
    Maven构建war项目添加版本号
    运行shell脚本报/bin/bash^M: bad interpreter错误排查方法
    Shell杀tomcat进程
    根据URL下载文件
    关闭Centos的自动更新
    CentOS下建立本地YUM源并自动更新
    为Linux服务器伪装上Windows系统假象
    ServerInfo.INI解密
    请教给终端推销域名的邮件该怎么写?
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8286480.html
Copyright © 2011-2022 走看看