zoukankan      html  css  js  c++  java
  • [做题记录-计数][AGC024E] Sequence Growing Hard

    题目描述

    给定 (n), (k), (m) , 问有多少个序列组 ((A_0,A_1,…,A_n)) 满足:序列 (A_i) 的元素个数为 (i) ; 所有元素都在 ([1,k]) 内; (forall iin[0,n)) , (A_i)(A_{i+1}) 的子序列且 (A_i) 的字典序小于 (A_{i+1}).

    输出在 (mod m) 意义下的答案.

    Solution

    又抄题解去了/kk。
    为什么这也可以上树啊/kk。
    可以发现本质上我们干的事情就是计数一个操作序列, 每次往序列里面加入一个数, 满足后面一个序列的字典序大于前面那个序列。那么每次就是考虑往序列里面某个数的前面放数, 要求放的数(x)小于这个数。
    但是这个东西直接(dp)并不好刻画, 考虑搞一棵操作树。这个树上的节点用一对数来刻画((id, val))。如果我们建立虚节点((0, 0)), 那么每次插入的时候就是选择一个(val < x)的位置, 在这个点下面挂一个点((now, x))。那么这样构造出来的一棵树直接与原序列对应。直接考虑对树计数即可。
    那么考虑设(dp_{i, j})表示(i)个节点的树, 根节点权值为(j)的树的个数, 有转移:

    [dp_{i, j} = sum_{p = 1}^{i - 1}inom{i - 2}{p - 1} imes dp_{i - p, j} imes sum_{q = j + 1}^kdp_{p, q} ]

    意义就是枚举根节点(id)最小的子树的大小, 分配标号以后再枚举这个子树根节点权值, 前缀和优化以后显然可以(O(n^2k))

    /*
    	QiuQiu /qq
      ____    _           _                 __                
      / __   (_)         | |               / /                
     | |  | |  _   _   _  | |  _   _       / /    __ _    __ _ 
     | |  | | | | | | | | | | | | | |     / /    / _` |  / _` |
     | |__| | | | | |_| | | | | |_| |    / /    | (_| | | (_| |
      \___\_ |_|  \__,_| |_|  \__, |   /_/      \__, |  \__, |
                                __/ |               | |     | |
                               |___/                |_|     |_|
    */
    
    #include <bits/stdc++.h>
    
    using namespace std;
    
    class Input {
    	#define MX 1000000
    	private :
    		char buf[MX], *p1 = buf, *p2 = buf;
    		inline char gc() {
    			if(p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MX, stdin);
    			return p1 == p2 ? EOF : *(p1 ++);
    		}
    	public :
    		Input() {
    			#ifdef Open_File
    				freopen("a.in", "r", stdin);
    				freopen("a.out", "w", stdout);
    			#endif
    		}
    		template <typename T>
    		inline Input& operator >>(T &x) {
    			x = 0; int f = 1; char a = gc();
    			for(; ! isdigit(a); a = gc()) if(a == '-') f = -1;
    			for(; isdigit(a); a = gc()) 
    				x = x * 10 + a - '0';
    			x *= f;
    			return *this;
    		}
    		inline Input& operator >>(char &ch) {
    			while(1) {
    				ch = gc();
    				if(ch != '
    ' && ch != ' ') return *this;
    			}
    		}
    		inline Input& operator >>(char *s) {
    			int p = 0;
    			while(1) {
    				s[p] = gc();
    				if(s[p] == '
    ' || s[p] == ' ' || s[p] == EOF) break;
    				p ++; 
    			}
    			s[p] = '';
    			return *this;
    		}
    	#undef MX
    } Fin;
    
    class Output {
    	#define MX 1000000
    	private :
    		char ouf[MX], *p1 = ouf, *p2 = ouf;
    		char Of[105], *o1 = Of, *o2 = Of;
    		void flush() { fwrite(ouf, 1, p2 - p1, stdout); p2 = p1; }
    		inline void pc(char ch) {
    			* (p2 ++) = ch;
    			if(p2 == p1 + MX) flush();
    		}
    	public :
    		template <typename T> 
    		inline Output& operator << (T n) {
    			if(n < 0) pc('-'), n = -n;
    			if(n == 0) pc('0');
    			while(n) *(o1 ++) = (n % 10) ^ 48, n /= 10;
    			while(o1 != o2) pc(* (--o1));
    			return *this; 
    		}
    		inline Output & operator << (char ch) {
    			pc(ch); return *this; 
    		}
    		inline Output & operator <<(const char *ch) {
    			const char *p = ch;
    			while( *p != '' ) pc(* p ++);
    			return * this;
    		}
    		~Output() { flush(); } 
    	#undef MX
    } Fout;
    
    #define cin Fin
    #define cout Fout
    #define endl '
    '
    
    using ll = long long;
    using pii = pair<int, int>;
    
    const int N = 300 + 5;
    
    int P;
    
    inline void pls(int &x, int y) { x += y; if(x >= P) x -= P; }
    inline void dec(int &x, int y) { x -= y; if(x < 0) x += P; }
    
    int C[N][N];
    
    void init(int n = 300) {
    	C[0][0] = 1;
    	for(int i = 1; i <= n; i ++) {
    		C[i][0] = 1;
    		for(int j = 1; j <= i; j ++) {
    			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
    		}
    	}
    }
    
    int n, k;
    int dp[N][N], s[N][N];
    
    signed main() {
    	cin >> n >> k >> P; init();
    	for(int i = 0; i <= k; i ++) dp[1][i] = 1;
    	for(int i = k; i >= 0; i --) s[1][i] = (s[1][i + 1] + dp[1][i]) % P;
    	for(int i = 2; i <= n + 1; i ++) {
    		for(int j = 0; j <= k; j ++) {
    			for(int p = 1; p <= i - 1; p ++) {
    				pls(dp[i][j], 1ll * C[i - 2][p - 1] * dp[i - p][j] % P * s[p][j + 1] % P);
    			}
    		}
    		for(int j = k; j >= 0; j --) s[i][j] = (s[i][j + 1] + dp[i][j]) % P;
    	}
    	cout << dp[n + 1][0] << endl;
    	return 0;
    }
    
  • 相关阅读:
    exe自启动的几种方式
    关于 CShellManager 的作用
    DLL 调用 对话框 以及 如何获取调用dll 应用程序(窗口程序)的窗口句柄
    VC++ 2010 创建高级Ribbon界面详解(4)
    HPU--1221 Fibonacci数列
    取一个数的前几位
    HDU--1875 畅通工程再续
    POJ--2485 Highways
    【模板】HDU--1233 畅通工程
    hdu--1856 More is better
  • 原文地址:https://www.cnblogs.com/clover4/p/15339673.html
Copyright © 2011-2022 走看看