zoukankan      html  css  js  c++  java
  • NOIP模拟测试⑨

    第⑨次考试会变成baka

    起床就感觉喉咙好痛,来了机房直接GG,摸了下腋下感觉药丸。

    昏了头考试,崩的有点厉害(

    本次考试题目顺序的设置充满恶意。。。

    Problem A: 随

    我一看提示原根就弃了,写了个(O(nmmod))的暴力,结果一个点都没跑过。。。。

    首先这是个假期望题,总方案数为(n^m),求出所有情况之和就可以了

    设f[i][j]为i次操作x值为j的次数,暴力转移一波。写出方程还能发现是矩阵乘法的形式,这部分就不说了,直接看正解,毕竟我考试连这几个都没写。

    题目提示我们用原根, 我们就用原根。用原根的幂就可以在p-1次方内遍历整个剩余系,我们可以用原根的幂的形式代替原来的a数组。

    这样,原来的(a_i * a_j * a_k)的形式就变成了(g^i * g ^j * g ^ k = g ^ {i + j + k}),乘法变成了加法。

    我们把原来的f变一下,表示i次操作变成原根的j次方的次数。(f[i][(i + j) \% (p - 1)]+=f[i-1][i] * f[i-1][j]).

    这玩意又能矩阵优化,还是循环矩阵,写个循环矩阵快速幂,(O(mod^2 logm)),完事了

    几个问题:

    1.所以原根是啥啊?
    其实题目给的挺清楚。

    2.原根咋求啊?
    这题数据范围这么小,从2到mod-2枚举一边就完事了
    其实数据范围大了也没事,原根一般都不大(小声)

    3.这题取模有三个模数,要注意
    mod是题目给的,在把a数组变为原根的幂的形式时用;
    mod-1是根据拓展欧拉定理,(a^b = a^{b % phi p} mod p, (b, p) = 1).在快速幂处理指数的时候用;
    1e9+7对与答案有关的数都用。

    原根真是个好东西,希望我能记住。

    /*
     ______                  ______   
    /                      /       
    |      |   |        |   |      |
    |      |   |        |   |      |
    |      |   |   /   |   |      |
    \______/   \__/  \__/   \______/
    */
    #include <bits/stdc++.h>
    #define ll long long
    
    const int MO = 1000000007, N = 100005;
    int n, m, p, rt;
    ll po[N], idx[N], a[N];
    
    struct Matrix {
    	ll a[1005];
    	friend Matrix operator *(Matrix x, Matrix y) {
    		Matrix z = {};
    		for (int i = 0; i <= p - 2; i++)
    			for (int j = 0; j <= p - 2; j++)
    				z.a[(i + j) % (p - 1)] = (z.a[(i + j) % (p - 1)] + x.a[i] * y.a[j]) % MO;
    		return z;
    	}
    	inline void clear() {
    		for (int i = 0; i <= p - 2; i++) a[i] = 0;		
    	}
    	Matrix qpow(int b) {
    		Matrix tmp, x = (*this);
    		tmp.clear(), tmp.a[idx[1]]++;
    		for (; b; b >>= 1, x = x * x)
    			if (b & 1) tmp = tmp * x;
    		return tmp;
    	}
    } mtx, ans;
    
    ll qpow(ll x, ll b, ll p) {
    	ll tmp = 1;
    	for (; b; b >>= 1, x = x * x % p)
    		if (b & 1) tmp = tmp * x % p;
    	return tmp;
    }
    
    inline int getrt(int x) {
    	for (int i = 2; i <= x; i++) {
    		ll tmp = 1; bool flg = 1;
    		for (int j = 1; j <= x - 2; j++) {
    			tmp = tmp * i % x;
    			if (tmp == 1) {
    				flg = 0; 
    				break;
    			}
    		}
    		if (flg) return i;
    	}
    }
    
    signed main() {
    	scanf("%d%d%d", &n, &m, &p);
    	rt = getrt(p); po[0] = 1;
    	for (int i = 1; i <= p - 2; i++)
    		po[i] = po[i - 1] * (ll) rt % p, idx[po[i]] = i;
    	for (int i = 1; i <= n; i++)
    		scanf("%lld", &a[i]), mtx.a[idx[a[i]]]++;
    	ans = mtx.qpow(m);
    	ll tot = 0;
    	for (int i = 0; i <= p - 2; i++)
    		tot = (tot + po[i] * ans.a[i]) % MO;
    	ll inv = qpow(n, (ll) m * (MO - 2), MO);
    	printf("%lld
    ", tot * inv % MO);
    	return 0;
    }
    

    Problem B: 单

    考试就写了个Subtask1,,,

    (sum = sum a_i) siz[x] 是x为根子树的每个节点的a和。

    很明显,每个点的贡献就是它子树内每个节点的siz和。

    Subtask 1应该是个换根DP,把1作为根节点,一遍dfs求出b[1],第二遍dfs求出b[]。转移:(b[y] = b[x] + sum - 2 * siz[x]),这个还是比较显然的。

    Subtask 2我考场上觉得应该是个高斯消元,但我万年没写过不会写,而且数据范围也不对,不是正解。

    正解是解方程,但不用搞死小圆

    换根的时候得到了(b[y] - b[x] = sum - 2 * siz[x])。能列出n-1个方程,但少了一个解不出来。b[1]搞不出来这种形式的式子。

    但我们可以暴力算b[1]啊!!(b_1 = sum limits_{i = 2}^n siz[i]).

    我们有n个方程了,可以解n个未知数了。暴力回代就完事了。

    /*
     ______                  ______   
    /                      /       
    |      |   |        |   |      |
    |      |   |        |   |      |
    |      |   |   /   |   |      |
    \______/   \__/  \__/   \______/
    */
    #include <bits/stdc++.h>
    #define ll long long
    
    const int N = 100000 + 2333;
    int T, n, ecnt, head[N], op;
    ll a[N], b[N], siz[N], sum, f[N];
    struct Edge {int y, nxt;} e[N << 1];
    
    inline void add_edge(int x, int y) {
    	e[++ecnt] = {y, head[x]}, head[x] = ecnt;
    }
    
    namespace case0 {
    	ll f[N];
    	
    	void OAO(int x, int fa) {
    		siz[x] = a[x], b[x] = 0;
    		for (int i = head[x], y = e[i].y; i; i = e[i].nxt, y = e[i].y)
    			if (y != fa)
    				OAO(y, x), siz[x] += siz[y], b[x] += b[y] + siz[y];
    	}
    
    	void OwO(int x, int fa) {
    		for (int i = head[x], y = e[i].y; i; i = e[i].nxt, y = e[i].y)
    			if (y != fa) b[y] = b[x] + sum - 2 * siz[y], OwO(y, x);
    	}
    
    	void QAQ() {
    		for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), sum += a[i];
    		OAO(1, 0), OwO(1, 0); 
    		for (int i = 1; i <= n; i++)
    			printf("%lld ", b[i]);
    		printf("
    ");
    	}
    }
    
    namespace case1 {
    	void QwQ(int x, int fa) {
    		for (int i = head[x], y = e[i].y; i; i = e[i].nxt, y = e[i].y)
    			if (y != fa) 
    				f[y] = b[y] - b[x], QwQ(y, x);
    	}
    
    	void OwO(int x, int fa) {
    		a[x] = siz[x] = (sum - f[x]) / 2;
    		for (int i = head[x], y = e[i].y; i; i = e[i].nxt, y = e[i].y)
    			if (y != fa) OwO(y, x), a[x] -= siz[y];
    	}
    
    	void QAQ() {
    		for (int i = 1; i <= n; i++) 
    			scanf("%lld", &b[i]);
    		QwQ(1, 0);
    		for (int i = 2; i <= n; i++) 
    			sum += f[i];
    		sum = (sum + 2 * b[1]) / (ll) (n - 1);
    		OwO(1, 0), a[1] = sum;
    		for (int i = 2; i <= n; i++)
    			a[1] -= a[i];
    		for (int i = 1; i <= n; i++)
    			printf("%lld ", a[i]);
    		printf("
    ");
    	}
    }
    
    void clear() {
    	memset(head, 0, sizeof(head));
    	ecnt = sum = 0;
    }
    
    signed main() {
    	scanf("%d", &T);
    	while (T--) {
    		scanf("%d", &n);
    		for (int i = 1, x, y; i < n; i++)
    			scanf("%d%d", &x, &y), add_edge(x, y), add_edge(y, x);
    		scanf("%d", &op);
    		if (op == 1) case1::QAQ();
    		if (op == 0) case0::QAQ();
    		clear();
    	}
    	return 0;
    }
    

    Problem C: 题

    T3最可做,wsl

    科学开题顺序:C -> B -> A(

    这个事情告诉我们要先把所有题看一遍,谁告诉你题目就一定要按照难度升序排列了(

    题中还有坑,subtask 0,1,3都是白给75分,然而我被卡在2一直没看3。。。。

    讲解按难度升序排列,不像出题人那么屑。

    subtask 0:

    枚举水平向右步数a,垂直向上步数c。答案为(C_n^a C_{n - a} ^ a C_{n - 2a} ^ c = frac{n!}{a! a! c! c!}).

    预处理阶乘和阶乘逆元,完事

    subtask 1:

    设向右步数a,向左步数b,这就像出入栈问题,必须保持 a >= b.所以答案就是Cat[n / 2].

    subtask 3:

    就是subtask 0和1合了起来,枚举水平向右步数a,可算出向上步数c,相当于在x轴和y轴都做一边subtask1。

    答案就是(C_n^{2a} Cat[a] Cat[c]).

    subtask 2:

    这问组合计数不能,我在考场上疯狂尝试容斥,结果容不出来。

    其实就是一个很简单的DP。设f[i]为第i步回到原点的方案数,枚举第一次回到原点的步数j,它满足subtask 1。(f[i] = f[i - j] Cat[j / 2 - 1] * 4).

    解释细节:Cat为什么下标要减1?j步内是不能回到原点的,可以类比出入栈,我们不能让栈为空。×4就是4个方向。

    看完题再开题!!!

    看完题再开题!!!

    /*
     ______                  ______   
    /                      /       
    |      |   |        |   |      |
    |      |   |        |   |      |
    |      |   |   /   |   |      |
    \______/   \__/  \__/   \______/
    */
    #include <bits/stdc++.h>
    #define ll long long
    
    const int MO = 1000000007, N = 1000000 + 233;
    int n, typ;
    
    ll ans, fac[N], inv[N], cat[N];
    
    ll Qpow(ll x, int b) {
    	ll ret = 1;
    	for (; b; b >>= 1, x = x * x % MO)
    		if (b & 1) ret = ret * x % MO;
    	return ret;
    }
    
    void init(int t) {
    	fac[0] = fac[1] = 1, cat[1] = 1, cat[0] = 1;
    	for (int i = 2; i <= t; i++) fac[i] = fac[i - 1] * i % MO;
    	for (int i = 0; i <= t; i++) inv[i] = Qpow(fac[i], MO - 2);
    	for (int i = 2; i <= t; i++) cat[i] = (cat[i - 1] * (4 * i - 2) % MO * Qpow(i + 1, MO - 2) % MO) % MO;
    }
    
    namespace case0 {
    	void QAQ() {
    		init(n);
    		for (int i = 0; i <= n; i += 2) {
    			int a = i / 2, c = (n - i) / 2;
    			ans = (ans + fac[n] * inv[a] % MO * inv[a] % MO * inv[c] % MO * inv[c] % MO) % MO;
    		}
    		printf("%lld
    ", ans);
    	}
    }
    
    namespace case1 {
    	void QAQ() {
    		init(n);
    		printf("%lld
    ", cat[n / 2]);
    	}
    }
    
    namespace case2 {
    	ll f[N];
    	void QAQ() {
    		init(n), n /= 2;
    		f[0] = 1, f[1] = 4;
    		for (int i = 2; i <= n; i++)
    			for (int j = 1; j <= i; j++)
    				f[i] = (f[i] + 4 * f[i - j] % MO * cat[j - 1]) % MO;
    		printf("%lld
    ", f[n]% MO);
    	}
    }
    
    namespace case3 {
    	void QAQ() {
    		init(n);
    		for (int i = 0; i <= n; i += 2) {
    			int a = i / 2, c = (n - i) / 2;
    			ans = (ans + fac[n] * inv[i] % MO * inv[n - i] % MO * cat[a] % MO * cat[c] % MO) % MO;
    		}
    		printf("%lld
    ", ans);	
    	}
    }
    
    signed main() {
    	scanf("%d%d", &n, &typ);
    	if (typ == 0) case0::QAQ();
    	if (typ == 1) case1::QAQ();
    	if (typ == 2) case2::QAQ();
    	if (typ == 3) case3::QAQ();
    	return 0;
    }
    

    抛开身体原因不谈,本次考试仍然暴露出了思维深度不足的大问题,比如T2想到高斯消元就跑了,难道真的想不到解方程吗?为什么就不再深入的思考一下呢?

    时间不多了,要努力啊。

  • 相关阅读:
    poj 2443
    codeforces 263D
    codeforces 263C
    uva 11080
    uva 11235
    uva 11748
    STL uva 11991
    (KM) uva 11383
    (树形DP) uva 10859
    codeforces 242C
  • 原文地址:https://www.cnblogs.com/gekoo/p/11256664.html
Copyright © 2011-2022 走看看