zoukankan      html  css  js  c++  java
  • 2019ICPC上海网络赛

    D - Counting Sequences I

    题意

    (n) , (1 le n le 3000)

    问满足 (sum_{i=1}^na_i = prod_{i=1}^na_i)

    的方案数,对 (1e9+7) 取模

    思路

    可以暴力打表,用排列公式

    [dfrac {N!} {a_1!a_2!...a_m!} ]

    加上发现的一些剪枝,可以在一分钟左右算出全部答案

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int mod = 1e9 + 7;
    const int N = 3005;
    typedef long long ll;
    
    ll fac[N], inv[N];
    int a[N], n;
    ll ans;
    
    ll qpow(ll a, ll b)
    {
    	ll ans = 1;
    	while (b)
    	{
    		if (b & 1) ans = ans * a % mod;
    		a = a * a % mod;
    		b >>= 1;
    	}
    	return ans;
    }
    
    void dfs(int pos, int u, int add, int pro)
    {
    	if (pos > n)
    	{
    		if (add == pro)
    		{
    			ll tmp = fac[n], cnt = 1;
    			for (int i = 2; i <= n; i++)
    				if (a[i] == a[i - 1]) cnt++;
    				else
    				{
    					tmp = tmp * inv[cnt] % mod;
    					cnt = 1;
    				}
    			tmp = tmp * inv[cnt] % mod;
    			ans = (ans + tmp) % mod;
    		}
    		return;
    	}
    	if (pro > 2*n) return;
    	if (u == 1 and (n - pos + 1) + add != pro) return;
    	if ((n - pos + 1) + add < pro) return;
    	for (int i = u; i >= 1; i--)
    	{
    		a[pos] = i;
    		dfs(pos + 1, i, add + i, pro * i);
    		//a[pos] = 0;
    	}
    }
    int main()
    {
    	//freopen("D:\1.txt", "w", stdout);
    	fac[0] = 1;
    	for (int i = 1; i < N; i++)
    		fac[i] = fac[i - 1] * i % mod;
    
    	inv[N - 1] = qpow(fac[N - 1], mod - 2);//阶乘的逆元
    	for (int i = N - 2; i >= 0; i--)
    		inv[i] = inv[i + 1] * (i + 1) % mod; 
    
    	
    	putchar('{');
    	for (n = 2; n <= 3000; n++)
    	{	
    		ans = 0;
    		dfs(1, n, 0, 1);
    		printf("%lld", ans);
    		if (n != 3000)
    			putchar(',');
    		else
    			putchar('}');
    	}
    
    	return 0;
    }
    

    F - Rhyme scheme

    题目大意

    用字符表示集合的划分

    给定 (n) 问第 (k) 小的划分

    思路

    image-20201110185239961

    如图,所有的叶子节点从左到右就是 $n = 4 $ 的时候的所有集合划分

    设状态表示 (f[n][i][j]) 表示的是总层数 (n) , 目前层数 (i) ,且由根节点到它的路径上最大的字母为 (j)点的集合 ,集合的属性是该节点下叶子节点的数目

    显然当 (i == n) 时,(f[n][i][j]=1) ,即到了最后一层,都是叶子节点。

    否则如上图蓝色标注的 (B) 节点,若 最大字母不变则有 (j) 棵相同的子树,否则加上一颗增加了的子树。

    (f[n][i][j] = f[n][i+1][j]*j + f[n][i+1][j+1])

    然后就可以直接用这些信息去找答案了,类似找 (k) 大这样。

    需要注意 (B_{26}) 爆了 $long long $

    #include<bits/stdc++.h>
    using namespace std;
    
    int T, n;
    __int128 k;
    __int128 f[30][30][30];
    void read(__int128& x) {
    	x = 0;
    	int f = 1;
    	char ch = getchar();
    	while (!(ch >= '0' && ch <= '9')) ch = getchar();
    	x = x * 10 + ch - '0';
    	while ((ch = getchar()) >= '0' && ch <= '9')
    		x = x * 10 + ch - '0';
    	x *= f;
    }
    
    void init() {
    	for (int n = 1; n <= 26; n++)
    		for (int i = n; i >= 1; i--)
    			if (i == n) {
    				for (int j = 1; j <= i; j++) f[n][i][j] = 1ll;
    			}
    			else {
    				for (int j = 1; j <= i; j++) f[n][i][j] = f[n][i + 1][j] * j + f[n][i + 1][j + 1];
    			}
    }
    
    int main() {
    	init();
    	scanf("%d", &T); int c = 0;
    	while (T--) {
    		scanf("%d", &n);
    		read(k);
    
    		printf("Case #%d: ", ++c);
    		int now = 0, j;
    		for (int i = 1; i <= n; i++) {
    			for (j = 1; j <= now; j++) {
    				if (k <= f[n][i][now]){
    					break;
    				}
    				k -= f[n][i][now];
    			}
    			putchar('A' + j - 1);
    			now = max(now, j);
    		}
    		puts("");
    	}
    }
    

    C - Triple

    题意

    给三个数组 (A,B,C) 问有多少个 ((i,j,k)) 使得

    (A_i,B_j,C_k) 中较小的两个数的和大于等于最大的数

    (1 le T le 100)

    (1 le A_i,B_i,C_i,n le 100,000)

    There are at most (20) test cases with (N>1000)

    思路

    用容斥思想,所有不和法的方案就是 较小的两数相加小于第三个数的方案。

    处理出 所有的 (A_i + B_j) ,然后对于每一个 (C_k) ,只要加上所有小于 (C_k) 的方案数就可以

    这里小数据用暴力,大数据用 多项式乘法

    /*
     * @Author: zhl
     * @Date: 2020-11-09 15:23:52
     */
    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i = a;i <= b;i++)
    #define mem(a,b) memset((a),(b),sizeof(a))
    
    using namespace std;
    
    typedef long long ll;
    const double pi = acos(-1.0);
    const int N = 6e5 + 10;
    
    struct cp {
    	double x, y;
    	cp() {}
    	cp(double _x, double _y) {
    		x = _x; y = _y;
    	}
    	cp operator + (cp b) {
    		return cp(x + b.x, y + b.y);
    	}
    	cp operator -(cp b) {
    		return cp(x - b.x, y - b.y);
    	}
    	cp operator *(cp b) {
    		return cp(x * b.x - y * b.y, x * b.y + y * b.x);
    	}
    };
    int rev[N];
    int bit = 0;
    int lim;
    void FFT(cp* a, int inv) {
    
    	for (int i = 0; i < lim; i++) {
    		if (i < rev[i]) {
    			swap(a[i], a[rev[i]]);
    		}
    	}
    
    	for (int mid = 1; mid < lim; mid <<= 1) {
    		cp temp(cos(pi / mid), inv * sin(pi / mid));
    		for (int i = 0; i < lim; i += mid * 2) {
    			cp omega(1, 0);
    			for (int j = 0; j < mid; j++, omega = omega * temp) {
    				cp x = a[i + j], y = omega * a[i + j + mid];
    				a[i + j] = x + y, a[i + j + mid] = x - y;
    			}
    		}
    	}
    }
    
    
    int T, n, A[N], B[N], C[N], tA[N], tB[N], tC[N];
    cp x[N], y[N];
    ll sum[N];
    
    ll solve_small(int* a, int* b, int* c) {
    	for (int i = 0; i <= c[n - 1]; i++)sum[i] = 0;
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < n; j++) {
    			sum[a[i] + b[j]]++;
    		}
    	}
    	ll ans = 0;
    	for (int i = 1; i <= c[n - 1]; i++)sum[i] += sum[i - 1];
    	for (int i = 0; i < n; i++) ans += sum[c[i] - 1];
    	return ans;
    }
    
    ll solve_big(int* a, int* b, int* c) {
    
    	for (int i = 0; i <= lim; i++)x[i] = cp(a[i], 0), y[i] = cp(b[i], 0);
    
    	FFT(x, 1); FFT(y, 1);
    	for (int i = 0; i <= lim; i++)x[i] = x[i] * y[i];
    	FFT(x, -1);
    
    	mem(sum, 0);
    	ll ans = 0;
    	for (int i = 0; i <= c[n - 1]; i++)sum[i] = signed(x[i].x / lim + 0.5);
    	for (int i = 1; i <= c[n - 1]; i++)sum[i] += sum[i - 1];
    	for (int i = 0; i < n; i++) ans += sum[c[i] - 1];
    	return ans;
    }
    
    int main() {
    	scanf("%d", &T); int c = 0;
    	while (T--) {
    		scanf("%d", &n);
    
    		mem(tA, 0); mem(tB, 0); mem(tC, 0);
    		lim = 1; bit = 0;
    		while (lim <= (2 * n))lim <<= 1, bit++;
    		mem(rev, 0);
    		for (int i = 0; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    
    		for (int i = 0; i < n; i++)scanf("%d", A + i), tA[A[i]]++; sort(A, A + n);
    		for (int i = 0; i < n; i++)scanf("%d", B + i), tB[B[i]]++; sort(B, B + n);
    		for (int i = 0; i < n; i++)scanf("%d", C + i), tC[C[i]]++; sort(C, C + n);
    
    		printf("Case #%d: ", ++c);
    		if (n <= 1000) {
    			printf("%lld
    ", 1ll * n * n * n - solve_small(A, B, C) - solve_small(A, C, B) - solve_small(B, C, A));
    		}
    		else {
    			printf("%lld
    ", 1ll * n * n * n - solve_big(tA, tB, C) - solve_big(tA, tC, B) - solve_big(tB, tC, A));;
    		}
    	}
    }
    
  • 相关阅读:
    优达学城数据分析师纳米学位——知识点总结1
    优达学城数据分析师纳米学位——第二课 jupyter notebook的使用
    优达学城数据分析师纳米学位——第一课总结
    产品经理学习笔记- 猿题库运营面试准备
    EXCEL 2010学习笔记—— 动态图表
    EXCEL 2010学习笔记 —— VLOOKUP函数 嵌套 MATCH 函数
    最长上升子序列模板
    不要62
    划分树简单介绍
    母函数基本应用
  • 原文地址:https://www.cnblogs.com/sduwh/p/13955492.html
Copyright © 2011-2022 走看看