「Summary」2021-11-27 模拟赛

T1「BZOJ3260」跳

Sol

这道题先把 \(C(x,y)\) 打出来,可以发现 ta 就是个组合数(杨辉三角)。

1     1     1     1     1     1     1     1     
1     2     3     4     5     6     7     8     
1     3     6     10    15    21    28    36    
1     4     10    20    35    56    84    120   
1     5     15    35    70    126   210   330   
1     6     21    56    126   252   462   792   
1     7     28    84    210   462   924   1716  
1     8     36    120   330   792   1716  3432

不难猜出结论:一定是先沿着第一行或第一列走,在横着或竖着走到 \((n,m)\)

所以要先区分 \(n,m\) 的大小,由于 \(n*m \leq 10^{12}\) ,所以 \(n,m\) 中较小的一个一定 \(\leq 10^6\)

\(x\)\(n,m\) 中较小者,\(y\) 为较大者。

写出式子然后再化简:
\(Ans=x+\sum\limits_{i=1}^y \binom{x+i}{i}\)
\(=x+\sum\limits_{i=1}^y \binom{x+i}{x}\)
\(=x+\binom{x+y+1}{x}\)

但是 \(n,m\) 还是很大,按正常方式求肯定不行。

回到阶乘式:\(\binom{n}{m}=\frac{n!}{m!(n-m)!}=\frac{n(n-1)...(n-m+1)}{m!}\)

可以发现,分子分母都只有 \(m\) 项,而本题中 \(y \leq 10^6\) ,所以直接循环求组合数就行了

Code

#include <cstdio>
#include <cstring>

#define Maxn 1000
#define LL long long
#define Mod 1000000007
#define rep(i, j, k) for(int i = (j); i <= (k); i ++)
#define per(i, j, k) for(int i = (j); i >= (k); i --)

LL N, M;

template < typename _T >
_T Min (_T x, _T y) { return x < y ? x : y; }
template < typename _T >
_T Max (_T x, _T y) { return x > y ? x : y; }

LL Pow (LL x, LL y) {
    LL ret = 1;
    for (LL i = y; i; i >>= 1) {
        if (i & 1) ret = ret * x % Mod;
        x = x * x % Mod;
    }
    return ret;
}

LL C (LL x, LL y) {
    LL ret = 1;
    for (LL i = x - y + 1; i <= x; i ++) ret = ret * (i % Mod) % Mod;
    for (LL i = 1; i <= y; i ++) ret = ret * Pow (i, Mod - 2) % Mod;
    return ret;
}

int main () {
    scanf ("%lld %lld", &N, &M);
    LL n = Max (N, M);
    LL m = Min (N, M);

    printf ("%lld", (C (n + m + 1, m) + (n % Mod)) % Mod);
    return 0;
}

T2「HAOI2010」计数

Sol

刚开始看到这个题 \(n \leq 10^{50}\) ,很自然的会想到数位DP,是可做的,但按我的思路状态不能很好的表示,所以无了。

问题实际上可以转化为给你 \(len\) 个可重复数字,求 \(n\) 在这些数字全排列中的次序。(有前导零的数如 \(0012\) 当作 \(12\)

若数字不重复,就是经典的康托展开问题了。这里有重复的,思路是几乎一样的。

康托展开的结果即是:

\(n=\overline{n_1 n_2 n_3 ... n_k}\)\(x_i=\sum\limits_{j=1}^{k} [n_j<n_i]\)

\(Ans'=\sum\limits_{i=1}^k x_i·(n-i)!\)

这里存在重复所以需将剩下 \((n-i)\) 位上的排列改为可重排列。

假设剩下 \((n-i)\) 上要填数字 \(i\) 的个数为 \(s_i\)\(T=\sum s_i\)

\(0\) 的方案为 \(\binom{T}{s_0}\)

\(1\) 的方案为 \(\binom{T-s_0}{s_1}\)

\(2\) 的方案为 \(\binom{T-s_0-s_1}{s_2}\)
\(...\)

故总方案为 \(\binom{T}{s_0} \binom{T-s_0}{s_1} ... \binom{T-s_0-...-s_7}{s_8}\)

所以答案为 \(Ans=\sum\limits_{i=1}^{len} n_i \binom{T}{s_{i,0}} \binom{T-s_{i,0}}{s_{i,1}} ... \binom{T-s_{i,0}-...-s_{i,7}}{s_{i,8}}\)

Code

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define Maxn 100
#define LL long long
#define rep(i, j, k) for(int i = (j); i <= (k); i ++)
#define per(i, j, k) for(int i = (j); i >= (k); i --)

int len;
int cnt[10];
char s[Maxn + 5];
LL C[Maxn + 5][Maxn + 5];

void Init (int N) {
    rep (i, 0, N) {
        C[i][0] = 1;
        rep (j, 1, N) {
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
        }
    }
}

LL Value (int n) {
    LL tot = 1;
    rep (i, 0, 9) {
        if (cnt[i]) {
            tot *= C[n][cnt[i]];
            n -= cnt[i];
        }
    }
    return tot;
}

int main () {
    scanf ("%s", s + 1);

    len = strlen (s + 1);

    Init (len);

    rep (i, 1, len) cnt[s[i] - '0'] ++;

    LL ans = 0;
    rep (i, 1, len) {
        rep (j, 0, s[i] - '0' - 1) {
            if (cnt[j]) {
                cnt[j] --;
                ans += Value (len - i);
                cnt[j] ++;
            }
        }
        cnt[s[i] - '0'] --;
    }

    printf ("%lld", ans);
    return 0; 
}

T3 「HDU 某某题」编码

Sol

这道题挺套路,容斥的基本题吧。

问题可以转化为:

\(\sum\limits_{i=1}^m a_i = k\) 其中 \(a_i \in [1,n-1]\)

考虑容斥 \(\geq n\) 的部分

枚举多少个数超过 \(n-1\) 。再将剩下的用隔板法计算贡献。

Code

#include <cstdio>
#include <cstring>

#define Maxn 200000
#define LL long long
#define Mod 998244353 
#define rep(i, j, k) for(int i = (j); i <= (k); i ++)
#define per(i, j, k) for(int i = (j); i >= (k); i --)

int T;
int n, m, k;
LL inv[Maxn + 5], jc[Maxn + 5];

void Init () {
    jc[0] = 1;
    rep (i, 1, Maxn) jc[i] = jc[i - 1] * i % Mod;
    inv[1] = 1;
    rep (i, 2, Maxn) inv[i] = (Mod - Mod / i) * inv[Mod % i] % Mod;
    inv[0] = 1;
    rep (i, 1, Maxn) inv[i] = inv[i - 1] * inv[i] % Mod;
}

LL C (int X, int y) {
    if (X < y) return 0;
    return jc[X] * inv[y] % Mod * inv[X - y] % Mod;
}

int Min (int x, int y) {
    return x < y ? x : y;
}

int main () {
    scanf ("%d", &T);

    Init ();

    while (T --) {
        scanf ("%d %d %d", &n, &m, &k);

        LL ans = 0;
        rep (i, 0, k / n) {
            LL res = C (m, i) * C (k - i * n + m - 1, m - 1) % Mod;
            if (i & 1) ans = (ans - res + Mod) % Mod;
            else ans = (ans + res) % Mod; 
        }

        printf ("%lld\n", ans);
    }
    return 0;
}

T4 「不清楚来源题」敌对

又是一道容斥题,思路不难想,但考场上记错时间了。。。

每个人有 \(5\) 个爱好,可以分别求出有 \(i\) 有个爱好相同的方案数 \(s_i\) ,容斥系数为 \((-1)^{i+1}\)

可以用 map 或者 hash 处理有多少人有 \(i\) 个爱好相同,最后容斥一下即可。(按 std 的方法写了个 hash 表 map + tire树)

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

#define Maxn 50000
#define Size 1000007
#define LL long long
#define rep(i, j, k) for(int i = (j); i <= (k); i ++)
#define per(i, j, k) for(int i = (j); i >= (k); i --)

int n, tot;
LL ret[10];
int now[10], a[Maxn + 5][10];

struct Node {
	int pre, vl, id;
	Node () {
	}	
	Node (int x, int y, int z) {
		pre = x, vl = y, id = z;
	}
} ;

struct Hash_Table {
	vector < Node > h[Size + 5];
	int Find (int pre, int vl) {
		int hsh = ((LL) pre * 13331 + vl) % Size;
		rep (i, 0, (int) h[hsh].size () - 1) {
			if (h[hsh][i].pre == pre && h[hsh][i].vl == vl) return h[hsh][i].id;
		}
		return -1;
	}
	int Insert (int pre, int vl) {
		int hsh = ((LL) pre * 13331 + vl) % Size;
		h[hsh].push_back (Node (pre, vl, ++ tot));
		return tot;
	}
} tab;

struct Tire {
	int ed[Maxn * 32 + 5];
	int Get (int len) {
		int P = 0;
		rep (i, 1, len) {
			int val = now[i];
			int u = tab.Find (P, val);
			if (u == -1) u = tab.Insert (P, val);
			P = u;
		} 
		return ed[P] ++;
	}
} tire;

int main () {
    scanf ("%d", &n);

    rep (i, 1, n) {
        rep (j, 1, 5) {
            scanf ("%d", &a[i][j]);
        }
        sort (a[i] + 1, a[i] + 1 + 5);
    }

    rep (p, 1, n) {
    	rep (i, 1, (1 << 5) - 1) {
    		int cnt = 0;
    		rep (j, 1, 5) {
    			if (i & (1 << (j - 1))) {
    				now[++ cnt] = a[p][j];
				}
			}
			ret[cnt] += tire.Get (cnt);
		}
	}
	
	LL ans = 0;
	rep (i, 1, 5) {
		if (i & 1) ans += ret[i];
		else ans -= ret[i];
	}
	
    printf ("%lld", 1ll * n * (n - 1) / 2 - ans);
    return 0;
}
posted @ 2021-12-13 21:57  PoisonNNN  阅读(22)  评论(0编辑  收藏  举报