• XIX Open Cup, Grand Prix of China【杂题】


    传送门:Yandex

    A Array

    给定长为 \(n\) 的正整数序列 \(a_1,\cdots,a_n\),对于所有将 \(a_x\) 替换为 \(y\) 使得序列变为 \(a_1',\cdots,a_n'\) 的方案,设 \(c_k\) 表示 \(a_1',\cdots,a_k'\) 中不同数的个数,求 \(|a_x-y|+\sum_{k=1}^nkc_k\) 的最小值。

    \(n\le 10^6\)\(a_i\le 10^9\)

    solution

    \(a_x\) 只能是第一次出现的数,若替换为之前出现过的数,则 \(a_x\) 到下一次出现位置的 \(c_k\) 减去 \(1\);若替换为之后出现过的数 \(a_y\),则 \(a_y\)\(a_x\) 的下一次出现位置的 \(c_k\) 减去 \(1\),离散化之后对值域维护前后缀 \(\min\),时间复杂度 \(\mathcal O(n\log n)\)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1000003;
    template<typename T>
    bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    int T, n, m, a[N], val[N], tmp[N], nxt[N];
    LL ans, res;
    bool vis[N]; 
    LL C2(int x){return x*(x-1ll)>>1;}
    set<int> S;
    struct BIT {
    	LL tr[N];
    	void clear(){memset(tr, 0x3f, m+1<<3);}
    	void upd(int p, LL v){for(;p <= m;p += p & -p) chmin(tr[p], v);}
    	LL qry(int p){LL res = 1e18; for(;p;p -= p & -p) chmin(res, tr[p]); return res;}
    } t1, t2;
    void solve(){
    	cin >> n; ans = res = 0;
    	memset(vis, 0, n + 1);
    	for(int i = 1;i <= n;++ i){cin >> a[i]; val[i] = a[i];}
    	sort(val + 1, val + n + 1);
    	m = unique(val + 1, val + n + 1) - val - 1;
    	fill(tmp + 1, tmp + m + 1, n + 1);
    	for(int i = n;i;-- i){
    		a[i] = lower_bound(val + 1, val + m + 1, a[i]) - val;
    		nxt[i] = tmp[a[i]]; vis[nxt[i]] = true; tmp[a[i]] = i;
    	}
    	S.clear();
    	for(int i = 1, j = 0;i <= n;++ i){if(!vis[i]) ++ j; res += (LL)i * j;}
    	for(int i = 1;i <= n;++ i) if(!vis[i]){
    		auto it = S.lower_bound(a[i]);
    		if(it != S.end()) chmin(ans, val[*it] - val[a[i]] + C2(i) - C2(nxt[i]));
    		if(it != S.begin()) chmin(ans, val[a[i]] - val[*--it] + C2(i) - C2(nxt[i]));
    		S.insert(a[i]);
    	}
    	t1.clear(); t2.clear();
    	for(int i = n;i;-- i) if(!vis[i]){
    		chmin(ans, t1.qry(m - a[i]) - val[a[i]] - C2(nxt[i]));
    		chmin(ans, t2.qry(a[i] - 1) + val[a[i]] - C2(nxt[i]));
    		t1.upd(m + 1 - a[i], C2(i) + val[a[i]]);
    		t2.upd(a[i], C2(i) - val[a[i]]);
    	}
    	printf("%lld\n", res + ans);
    }
    int main(){
    	ios::sync_with_stdio(false);
    	cin >> T; while(T --) solve();
    }
    

    C Cut the Plane

    给定平面上 \(n\) 个点,使用 \(\lceil\frac n2\rceil\) 条直线将平面划分使得其中任意 \(2\) 个点都不在同一区域。

    \(n\le 100\)\(-1000\le x,y\le 1000\),任意 \(3\) 个点不共线。

    solution

    用一条直线将这 \(n\) 个点分为 \(\lfloor\frac n2\rfloor\) 个点和 \(\lceil\frac n2\rceil\) 个点的两部分,每次求两部分的凸包公切线,把两个切点分别割出去即可。

    不想写了,贴个 std,,

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 105;
    
    struct Point
    {
        int x, y;
        Point() {}
        Point(int _x, int _y) : x(_x), y(_y) {}
        Point operator+(const Point &t) const
        {
            return Point(x + t.x, y + t.y);
        }
        Point operator-(const Point &t) const
        {
            return Point(x - t.x, y - t.y);
        }
        Point operator*(const int &t) const
        {
            return Point(x * t, y * t);
        }
        int operator*(const Point &t) const
        {
            return x * t.y - y * t.x;
        }
        bool operator<(const Point &t) const
        {
            return x == t.x ? y < t.y : x < t.x;
        }
    } p[MAXN];
    
    bool usd[MAXN];
    
    void split(Point a, Point &b)
    {
        if (b < a)
            swap(a, b);
        Point c = a - (b - a) * 10000 + (a.x == b.x ? Point(1, 0) : Point(0, -1));
        Point d = b + (b - a) * 10000 + (a.x == b.x ? Point(-1, 0) : Point(0, 1));
        printf("%d %d %d %d\n", c.x, c.y, d.x, d.y);
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for (int ca = 0; ca < T; ca++)
        {
            int n;
            scanf("%d", &n);
            for (int i = 0; i < n; i++)
                scanf("%d%d", &p[i].x, &p[i].y), usd[i] = 0;
            if (n == 1)
            {
                printf("-1000000000 1000000000 1000000000 1000000000\n");
                continue;
            }
            sort(p, p + n);
            Point o = p[0];
            sort(p + 1, p + n, [o](const Point &a, const Point &b)
                 { return (a - o) * (b - o) < 0; });
            split(p[0], p[n / 2]);
            for (int _ = 0; _ < (n - 1) / 2; _++)
            {
                int l, r;
                for (l = 0; l < n / 2 && usd[l]; l++);
                for (r = n / 2; r < n && usd[r]; r++);
                while (1)
                {
                    bool c = 0;
                    for (int i = 0; i < n / 2; i++)
                        if (!usd[i] && (p[i] - p[r]) * (p[l] - p[r]) < 0)
                            l = i, c = 1;
                    for (int i = n / 2; i < n; i++)
                        if (!usd[i] && (p[i] - p[l]) * (p[r] - p[l]) > 0)
                            r = i, c = 1;
                    if (!c)
                        break;
                }
                usd[l] = 1, usd[r] = 1;
                for (l = 0; l < n && usd[l]; l++);
                for (int i = 0; i < n; i++)
                    if (!usd[i] && (p[i] - p[r]) * (p[l] - p[r]) < 0)
                        l = i;
                split(p[l], p[r]);
            }
        }
        return 0;
    }
    

    D Edges Counting

    定义好图是每个连通块至多有一个简单环的简单无向图。

    给定正整数 \(p\),对所有 \(n\in[1,3000]\)\(n\) 个点有标号的好图的环边条数之和\(\bmod p\)

    \(p\le 2^{30}\)

    solution

    \(\displaystyle A(x):=\sum_{n\ge 1}\frac{n^{n-2}x^n}{n!}\) 是有标号无根树数量的 EGF,\(\displaystyle B(x):=\sum_{n\ge 1}\frac{n^{n-1}x^n}{n!}\) 是有标号有根树数量的 EGF,\(\displaystyle C(x):=\frac 12\sum_{n\ge 3}B(x)^n=\frac{B(x)^3}{2(1-B(x))}\) 是有标号基环树的环边数量的 EGF,\(\displaystyle D(x):=\sum_{n\ge 3}\frac{B(x)^n}{2n}=\int\frac{B(x)^2B'(x)}{2(1-B(x))}\) 是有标号基环树数量的 EGF,所求即为 \(C(x)\exp(A(x)+D(x))\),写个 \(\mathcal O(n^2)\) 全家桶即可。

    麻烦的一点是计算 \(B(x)^2/2\),你需要把模数乘 \(2\),,

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned u32;
    typedef unsigned long long LL;
    const int n = 3000, N = n+3;
    int T, x;
    u32 mod, C[N][N], a[N], b[N], _b[N], c[N], d[N], e[N], f[N], g[N], h[N];
    u32 ksm(u32 a, u32 b){
    	u32 res = 1;
    	for(;b;b >>= 1, a = (LL)a * a % mod)
    		if(b & 1) res = (LL)res * a % mod;
    	return res;
    }
    void mul(u32 *a, u32 *b, u32 *c){
    	for(int i = 0;i <= n;++ i)
    		for(int j = 0;j <= i;++ j)
    			a[i] = (a[i] + (LL)C[i][j] * b[j] % mod * c[i-j]) % mod;
    }
    void div(u32 *a, u32 *b, u32 *c){
    	for(int i = 0;i <= n;++ i){
    		a[i] = b[i];
    		for(int j = 0;j < i;++ j)
    			a[i] = (a[i] + (LL)C[i][j] * a[j] % mod * c[i-j]) % mod;
    	}
    }
    void Exp(u32 *g, u32 *f){
    	g[0] = 1;
    	for(int i = 0;i < n;++ i)
    		for(int j = 0;j <= i;++ j)
    			g[i+1] = (g[i+1] + (LL)C[i][j] * f[j+1] % mod * g[i-j]) % mod;
    }
    int main(){
    	ios::sync_with_stdio(false);
    	cin >> T >> mod;
    	mod *= 2;
    	for(int i = 0;i <= n;++ i)
    		for(int j = *C[i] = 1;j <= i;++ j){
    			C[i][j] = C[i-1][j] + C[i-1][j-1];
    			if(C[i][j] >= mod) C[i][j] -= mod;
    		}
    	a[1] = b[1] = 1;
    	for(int i = 2;i <= n;++ i){a[i] = ksm(i, i-2); b[i] = (LL)a[i] * i % mod;}
    	mul(c, b, b);
    	for(int i = 2;i <= n;++ i) c[i] >>= 1;
    	mul(d, b, c);
    	div(e, d, b);
    	for(int i = 0;i <= n;++ i) _b[i] = b[i+1];
    	memset(d, 0, sizeof d);
    	mul(d, _b, c);
    	div(f, d, b);
    	for(int i = 1;i <= n;++ i){
    		a[i] += f[i-1];
    		if(a[i] >= mod) a[i] -= mod;
    	}
    	Exp(g, a); mul(h, e, g); mod >>= 1;
    	while(T --){cin >> x; printf("%u\n", h[x] >= mod ? h[x] - mod : h[x]);}
    }
    

    E Equanimous

    \(f(n)\) 表示在十进制下对 \(n\) 的每个数位赋 \(\pm 1\) 系数求和的绝对值最小值。

    \(T\) 次询问 \([L,R]\),对每个 \(i\in[0,9]\) 求使得 \(f(n)=i\)\(n\in[L,R]\) 之和\(\bmod(10^9+7)\)

    \(T\le 10^4\)\(L\le R\le 10^{100}\)

    solution

    考虑怎么求 \(f(n)\),直接 dp 设 \(f_{i,j}\) 表示考虑前 \(i\) 位,和的绝对值是否可以为 \(j\),下述引理表明存在常数 \(K\) 使得仅需考虑 \(j\le K\) 的情况。

    引理. 对于仅含 \(1,2,\cdots,w\) 的多重集 \(A,B\),若 \(A,B\) 的元素之和均 \(\ge w(w-1)\),则 \(A,B\) 分别存在子集使得元素之和相同。

    \(|A|=w-1\),则 \(A\)\(w-1\)\(w\) 构成,若 \(|B|=w-1\) 则结论显然成立,否则 \(|B|\ge w\),取其中 \(w\) 个元素排成一列,设前缀和为 \(b_0,b_1,\cdots,b_w\),由抽屉原理知存在 \(b_i,b_j\)\(w\) 的余数相同,则结论成立。

    \(|A|,|B|\ge w\),分别取其中 \(w\) 个元素排成一列,设前缀和分别为 \(a_1,\cdots,a_w\)\(b_1,\cdots,b_w\),不妨设 \(a_w\le b_w\),令 \(p_i\) 表示使得 \(b_{p_i}\ge a_i\) 的最小 \(p_i\),则 \(b_{p_i}-a_i\in[0,w)\),若存在 \(b_{p_i}=a_i\) 则结论成立,否则由抽屉原理知存在 \(i,j\) 使得 \(b_{p_i}-a_i=b_{p_j}-a_j\),结论也成立。

    推论. \(K=w^2-1\)

    设序列为 \(s_1,\cdots,s_k\) 以及最小的 \(p\) 使得 \(s_1+\cdots+s_p\ge w^2\),考虑 \(s_1,\cdots,s_p\) 中取加号的元素 \(d_1,\cdots,d_l\),取元素之和 \(\in[w(w-1),w^2)\) 的后缀作为 \(A\)\(s_{p+1},\cdots,s_k\) 中取减号的元素作为 \(B\),应用上述引理找到 \(U\subset A\)\(V\subset B\) 使得元素之和相同,将 \(U,V\) 中对应元素的符号取反,则 \(|s_1+\cdots+s_k|\) 保持不变,对 \(i\in[1,p)\) 仍有 \(|s_1+\cdots+s_i|<w^2\),且 \(|s_p|\) 会变小。不断重复上述操作直到停止即可。

    打表发现 \(f_i\) 作为 bitset 仅有不超过 \(2\times 10^5\) 种情况,可以建出 DFA,在 \(n\) 之后加数位相当于 DFA 上走一步,最后走到的状态对应 \(f(n)\) 的值即为所求。

    \(f(n)\) 的值作为类别,做 DFA 最小化后仅有 \(S=715\) 种状态,剩下的部分就是数位 dp 了,时间复杂度 \(\mathcal O((S+T)d^2\log R)\)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int mod = 1e9 + 7, ch[715][11] = { /* Cute DFA */ };
    int T, sum[101][715][10], cnt[101][715][10], pw[101], ans[2][10];
    void qmo(int &x){x += x >> 31 & mod;}
    void calc(const char *s, int len, int *ans){
    	memset(ans, 0, 40);
    	int u = 103, tmp = 0;
    	for(int i = len-1;~i;-- i){
    		for(int j = 0;j < 10;++ j)
    			for(int c = 1;c <= s[i] - 48;++ c)
    				ans[j] = (ans[j] + (tmp + (c-1ll) * pw[i]) % mod * cnt[i][ch[u][c]][j] + sum[i][ch[u][c]][j]) % mod;
    		u = ch[u][s[i] - 47];
    		tmp = (tmp + (s[i] - 48ll) * pw[i]) % mod;
    	}
    }
    char s1[105], s2[105];
    int main(){
    	scanf("%d", &T); *pw = 1;
    	for(int i = 0;i < 100;++ i) pw[i+1] = (LL)pw[i] * 10 % mod;
    	for(int i = 0;i < 715;++ i) cnt[0][i][ch[i][0]] = 1;
    	for(int i = 0;i < 100;++ i)
    		for(int j = 0;j < 715;++ j)
    			for(int k = 0;k < 10;++ k)
    				for(int c = 1;c <= 10;++ c){
    					qmo(cnt[i+1][j][k] += cnt[i][ch[j][c]][k] - mod);
    					qmo(sum[i+1][j][k] += sum[i][ch[j][c]][k] - mod);
    					sum[i+1][j][k] = (sum[i+1][j][k] + cnt[i][ch[j][c]][k] * (c-1ll) % mod * pw[i]) % mod;
    				}
    	while(T --){
    		scanf("%s%s", s1, s2);
    		int l1 = strlen(s1), l2 = strlen(s2);
    		reverse(s1, s1 + l1); reverse(s2, s2 + l2);
    		for(int i = 0;;++ i)
    			if(s2[i] == '9') s2[i] = '0';
    			else {++ s2[i]; break;}
    		if(s2[l2]) s2[l2++] = '1';
    		calc(s2, l2, ans[1]); calc(s1, l1, ans[0]);
    		for(int i = 0;i < 10;++ i){
    			qmo(ans[1][i] -= ans[0][i]);
    			printf("%d%c", ans[1][i], " \n"[i == 9]);
    		}
    	}
    }
    

    G Mysterious Triple Sequence

    给定正整数 \(p\) 和三元组序列 \(\{(a_k,b_k,c_k)\}_{k=0}^\infty\) 如下:

    • \((a_0,b_0,c_0)=(2,1,0)\)
    • \((a_{k+1},b_{k+1},c_{k+1})=(a_k^2+b_k^2,a_kb_k+b_kc_k,b_k^2+c_k^2)\)

    \(n\) 次询问 \(x,y,z,m\) 表示求最小的 \(k\ge m\) 使得 \((a_k,b_k,c_k)=(x,y,z)\pmod p\),需判断无解。

    \(n\le 5000\)\(p\le 2^{30}\)\(m\le 10^{18}\)

    solution

    \(f_0=0\)\(f_1=1\)\(f_{k+2}=2f_{k+1}+f_k\),则 \((a_k,b_k,c_k)=(f_{2^k+1},f_{2^k},f_{2^k-1})\)

    设模 \(p\) 意义下循环节为 \(L(p)\),类似 Pisano Period,当 \(p\) 为质数时,若 \((2\mid p)=1\)\(p\equiv\pm 1\pmod 8\)\(L(p)\mid(p-1)\),否则 \(L(p)\mid2(p+1)\);且 \(L(p^k)\mid p^{k-1}L(p)\)\(L(\prod p_i^{\alpha_i})=\text{lcm}\{L(p_i^{\alpha_i})\}\)

    先求 \(u\) 使得 \(f_{u-1}=z\)\(f_u=y\),然后求 \(k\) 使得 \(2^k\equiv u\pmod{L(p)}\),两个 BSGS 即可,时间复杂度 \(\mathcal O(\sqrt{np})\)

    H Inner Product

    给定两棵 \(n\) 个点的树,边带正权,求 \(\sum_{u=1}^n\sum_{v=1}^n\text{dis}_1(u,v)\cdot\text{dis}_2(u,v)\)\(10^9+7\) 的值。

    \(n\le 10^5\)\(w\le 10^9\)

    solution

    对第一棵树点分治,在第二棵树建虚树做 dp 统计答案

    I Counting Polygons

    Burnside 引理板子。

    J Square Graph

    给定正整数序列 \(a_1,\cdots,a_n\)\(w_1,\cdots,w_{\lfloor n/2\rfloor}\),构造 \(n\) 个点的无向图如下:对所有长为 \(2k\) 的平方子串,前一半向后一半对应位置连权值为 \(w_k\) 的边。求最小生成树边权之和。

    \(n\le 3\cdot 10^5\)\(a_i\le n\)\(w_i\le 10^9\)

    solution

    优秀的拆分 + 萌萌哒

    K Three Dimensions

    给定非负整数 \(X_1,Y_1,Z_1,X_2,Y_2,Z_2\),对 \([0,X_i]\times[0,Y_i]\times[0,Z_i]\) 的一对点 \((x_i,y_i,z_i)\),求 \(\max\{|x_1-x_2|,|y_1-y_2|,|z_1-z_2|\}\oplus x_1\oplus y_1\oplus z_1\oplus x_2\oplus y_2\oplus z_2\) 之和模 \(2^{30}\) 的值。

    \(X,Y,Z\le 10^9\)

    solution

    咕咕咕。

  • 相关阅读:
    动态规划(DP计数):HDU 5117 Fluorescent
    动态规划(DP计数):HDU 5116 Everlasting L
    动态规划(区间DP):HDU 5115 Dire Wolf
    数学(扩展欧几里得算法):HDU 5114 Collision
    二叉树面试题
    linux eval命令
    关于Shell中命令替换$(...)与后置引用`...`的不同
    linux set,env和export
    linux crontab 命令
    排序
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/16141068.html
Copyright © 2020-2023  润新知