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

咕咕咕。

posted @ 2022-04-13 16:56  mizu164  阅读(280)  评论(0编辑  收藏  举报