zoukankan      html  css  js  c++  java
  • [ARC100F] Colorful Sequences

    [题目链接]

    https://atcoder.jp/contests/arc100/tasks/arc100_d

    [题解]

    首先考虑容斥 , 用存在一个 (K) - 连续段的答案减去不存在 (K) - 连续段的答案。

    显然 , 总方案数为 ((N - M + 1) cdot K ^ {N - M})

    考虑不合法的方案数 , 不妨分三类情况讨论 :

    (1.) (A) 中已经存在一个连续段了 , 此时答案为 (0)

    (2.) (A) 中有两个相同的元素 , 那么对于序列中一个与 (A) "重合" 的子串 , 其不可能 "跨过" 一个连续段。 不妨设 (f_{i , j}) 表示 (i) 个数 , 末尾一段极长且互不相等的序列的最大长度是 (j) 的方案数。转移的时候只需考虑是否加一个新的元素或从某一位置 "断开" 即可。注意每次转移都是对一个连续区间贡献 , 于是可以差分前缀和优化从而做到 (O(N ^ 2))。对于首尾分别做一遍这样的 (DP) , 答案显然是一个卷积的形式。

    (3.) (A) 中没有相同的元素 , 那么必然满足 (K > M) , 考虑转化 , 原问题等价于长度为 (N) 的所有合法序列中有多少个 (K) - 连续段 , 注意这样是 “重新标号" 过的 , 因此准确的答案还需除以 (frac{K!}{(K - M)!})。 这个问题同样可以用 (DP) 解决 , 时间复杂度 (O(N ^ 2))
    , 不赘述了。

    [代码]

    #include <bits/stdc++.h>
     
    using namespace std;
     
    typedef long long LL;
     
    const int MK = 410 , MN = 30010 , mod = 1e9 + 7;
      
    int N , K , M , ans , X[MN] , Y[MN] , fac[MN] , ifac[MN] , occ[MN] , A[MN] , g[MN][MK] , 
        f[MN][MK];
    inline void inc(int &x , int y) {
        x = x + y < mod ? x + y : x + y - mod;
    }
    inline void dec(int &x , int y) {
        x = x - y >= 0 ? x - y : x - y + mod;
    }
    inline int qPow(int a , int b) {
        int c = 1;
        for (; b; b >>= 1 , a = 1LL * a * a % mod) if (b & 1) c = 1LL * c * a % mod;
        return c;
    }
    inline void init(int n) {
        fac[0] = 1;
        for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
        ifac[n] = qPow(fac[n] , mod - 2);
        for (int i = n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
        return;
    }
    inline bool check() {
        int j = 1 , cur = 0;
        for (int i = 1; i <= M; ++i) {
        	 if (!occ[A[i]]) ++cur; ++occ[A[i]];
           while (occ[A[i]] > 1) --occ[A[j++]];
           if (i - j + 1 == K && cur == K) return 1;
        }
        return 0;
    }
    inline bool check2() {
        memset(occ , 0 , sizeof(occ));
        for (int i = 1; i <= M; ++i)
    				if (occ[A[i]]) return 1;
    				else ++occ[A[i]];
        return 0;   
    }
    inline int DP1() {
        g[0][0] = 1; int ret = 0;
        for (int i = 0; i < N; ++i) {
            for (int p , q , j = 0; j < K; ++j) {
                p = 1ll * f[i][j] * (K - j) % mod , 
    	    			q = 1ll * g[i][j] * (K - j) % mod;
    	     			if (j + 1 < K) {
    								inc(f[i + 1][j + 1] , p) , dec(f[i + 1][j + 2] , p);
    								inc(g[i + 1][j + 1] , q) , dec(g[i + 1][j + 2] , q);
    	    			}
    	    			inc(f[i + 1][1] , f[i][j]) , inc(g[i + 1][1] , g[i][j]);
    	    			dec(f[i + 1][j + 1] , f[i][j]) , dec(g[i + 1][j + 1] , g[i][j]);
            }
            for (int j = 1; j < K; ++j) {
                inc(g[i + 1][j] , g[i + 1][j - 1]);
    	      		inc(f[i + 1][j] , f[i + 1][j - 1]);
            } 
            for (int j = M; j < K; ++j) 
    	    			inc(f[i + 1][j] , g[i + 1][j]);
        }
        for (int i = 1; i < K; ++i) inc(ret , f[N][i]);
        return ret;
    }
    inline void DP2(int res[MN] , int s[MN][MK] , int type) {
        memset(occ , 0 , sizeof(occ));
        if (!type) {
            for (int i = 1; i <= M; ++i)
    			      if (!occ[A[i]]) ++occ[A[i]];
    		        else { s[0][i - 1] = 1; break; }
        } else {
    	    	for (int i = M; i >= 1; --i)
    			     if (!occ[A[i]]) ++occ[A[i]];
    			     else { s[0][M - i] = 1; break; }
        }
        for (int i = 0; i < N - M; ++i) {
    	    	for (int val , j = 1; j < K; ++j) {
    	         val = 1ll * s[i][j] * (K - j) % mod;
    			     if (j + 1 < K) {
    				 			inc(s[i + 1][j + 1] , val);
    				 			dec(s[i + 1][j + 2] , val);
    			     }
    			     inc(s[i + 1][1] , s[i][j]);
    		       dec(s[i + 1][j + 1] , s[i][j]);
    		    }
    		    for (int j = 1; j < K; ++j) {
    		        inc(s[i + 1][j] , s[i + 1][j - 1]);
    			     	inc(res[i + 1] , s[i + 1][j]);
    				}
       }
    }
    int main() {
        
        scanf("%d%d%d" , &N , &K , &M); int mx = max(max(N , K) , M) + 1; init(mx);
        for (int i = 1; i <= M; ++i) scanf("%d" , &A[i]);
        int ans = 1LL * (N - M + 1) * qPow(K , N - M) % mod;
        if (check()) {
    			 	printf("%d
    " , ans);
    				return 0;
        }
        if (check2()) {
    	       X[0] = Y[0] = 1;
    	       DP2(X , f , 0) , DP2(Y , g , 1);
    	       for (int i = 0; i <= N - M; ++i) 
    		   		  dec(ans , 1ll * X[i] * Y[N - M - i] % mod);
        } else dec(ans , 1ll * DP1() * ifac[K] % mod * fac[K - M] % mod);
        assert(ans >= 0 && ans < mod);
        printf("%d
    " , ans);
        return 0;
    }
  • 相关阅读:
    Java_适配器模式
    linux常用命令整理
    (转)使用隐藏的iframe 隐藏form提交,仿AJax无刷新提交,可以实现无刷新上传文件
    mysql添加并返回主键
    学习RMI
    关于bcprov-jdk16
    JavaScript在页面中的引用方法
    通过CFX发布WebService(一)
    字符串和json数据的转换
    MD5 加密与解密
  • 原文地址:https://www.cnblogs.com/evenbao/p/14321095.html
Copyright © 2011-2022 走看看