zoukankan      html  css  js  c++  java
  • 【atcoder


    description

    给定 (N) 个白球排成一行,再给定长度为 (K) 的仅由 'r', 'b' 组成的字符串 (s),分别表示红色与蓝色。

    执行 (K) 次染色操作,第 (i) 次任意选择区间 ([L, R])(可以为空)并染上 (s_i) 所对应的颜色,新颜色将会覆盖原颜色。
    额外限制:不能将白色直接涂成蓝色,即蓝色只能涂在红球上。

    求最终可以得到序列的不同种类数。

    problem link。

    solution

    考虑最终序列的结构:它被白色段分割成了若干有色段,每个有色段的涂色是独立的(不存在操作跨越两个有色段)。

    那么考虑得到某个有色段所需的有效操作。手玩一下发现:

    (1)如果全为红色,则只需要一次 'r' 操作。

    (2)否则,至少需要一次 'r' 操作 + 一次 'b' 操作。
    在这种情况下,舍去首尾可能有的红色,此时再加入任意 (连续同色段的个数 - 1) / 2 次操作即可构造出这一有色段。

    因此,一个最终序列是否合法只与每个有色段所需有效次数有关。

    我们用状态 ({s_i}) 表示一种序列,含义为 “有效次数为 (i) 的有色段有 (s_i) 个”。
    状态数量粗略估计为整数分拆,可以剪枝剪掉一些,最后合法的约有 (4.2 imes 10^5)

    接下来只需要判状态是否可以被构造,与求每种状态对应最终序列的数量即可。


    考虑怎么判某个状态合法。显然先匹配 'r' + 'b' 再匹配 'r'。

    匹配 'r' + 'b' 可以从前往后贪心地定位前两个字符 'r' + 'b'。然后从后往前贪心地分配:越靠后的应取有效次数越少的同色段;尽量先分配 'b' 再分配 'r'。

    最后判剩下的 'r' 个数是否足够即可。


    考虑一个状态对应的方案数怎么算。首先对 ({s_i}) 求个可重排,表示有色段的位置顺序。

    考虑同色段的长度,有些同色段必须存在(长度 > 0),有些同色段可有可无(长度 ≥ 0)。
    因此方案数等于 (sum x_i = n) 的解数,其中某些 (x_i>0),某些 (x_igeq 0)。经典组合问题。

    code

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define rep(i, x, n) for(int i=x;i<=n;i++)
    #define pr make_pair
    #define fi first
    #define se second
    
    const int MOD = int(1E9) + 7;
    const int MAX = 420000;
    
    inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
    inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
    inline int mul(int x, int y) {return (int) (1LL * x * y % MOD);}
    int mpow(int b, int p) {
    	int r; for(r=1;p;p>>=1,b=mul(b,b))
    		if( p & 1 ) r = mul(r, b);
    	return r;
    }
    
    struct node{int x[40], cnt;}st[MAX + 5];
    
    int l[75], tot, N, K;
    void dfs(int s, node a, int x) {
    	st[++tot] = a;
    	for(int i=x;i<=N&&l[i]<=s;i++)
    		a.x[a.cnt++] = i, dfs(s - l[i], a, i), a.x[--a.cnt] = i;
    }
    
    int f[MAX + 5], fct[505], ifct[505], c[505][505];
    void init() {
    	rep(i, 0, 500) rep(j, 0, i)
    		c[i][j] = (j == 0 ? 1 : add(c[i - 1][j], c[i - 1][j - 1]));
    	fct[0] = 1; rep(i, 1, 500) fct[i] = mul(fct[i - 1], i);
    	ifct[500] = mpow(fct[500], MOD - 2);
    	for(int i=499;i>=0;i--) ifct[i] = mul(ifct[i + 1], i + 1);
    	
    	l[1] = 2; rep(i, 2, N) l[i] = (i - 1) << 1; dfs(N, (node){}, 1);
    	rep(i, 1, tot) {
    		int a = 1, b = 0, t = 1, p = 1;
    		rep(j, 1, st[i].cnt - 1) {
    			if( st[i].x[j] != st[i].x[j-1] )
    				t = mul(t, ifct[p]), p = 1;
    			else p++;
    		}
    		t = mul(t, ifct[p]);
    		rep(j, 0, st[i].cnt - 1) {
    			b += l[st[i].x[j]];
    			if( st[i].x[j] != 1 ) a += 2;
    		}
    		f[i] = mul(mul(fct[st[i].cnt], t), c[N + a - 1][a + b - 1]);
    	}
    //	printf("%d
    ", tot);
    }
    
    int tg[75], q[75]; char s[75];
    int main() {
    	scanf("%d%d%s", &N, &K, s + 1), N++, init();
    	
    	int ans = 0;
    	rep(i, 1, tot) {
    		int cnt1 = 0, pos = -1;
    		rep(j, 0, st[i].cnt - 1) {
    			if( st[i].x[j] != 1 ) {
    				pos = j;
    				break;
    			} else cnt1++;
    		}
    		
    		int hd = 1, tl = 0, p = st[i].cnt - cnt1;
    		rep(j, 1, K) tg[j] = -1;
    		for(int j=1;p&&j<=K;j++) {
    			if( s[j] == 'r' ) q[++tl] = j;
    			else if( hd <= tl ) tg[q[hd++]] = 0, tg[j] = 1, p--;
    		}
    		
    		int cnt[2] = {}; bool flag = true;
    		for(int j=K;j>=1;j--) {
    			if( tg[j] == -1 ) cnt[s[j] == 'r' ? 0 : 1]++;
    			else if( tg[j] == 1 ) {
    				int tmp = st[i].x[pos] - 2;
    				while( cnt[1] && tmp ) cnt[1]--, tmp--;
    				while( cnt[0] && tmp ) cnt[0]--, tmp--;
    				if( tmp ) {
    					flag = false;
    					break;
    				}
    				pos++;
    			}
    		}
    		
    		if( flag && cnt[0] >= cnt1 && p == 0 ) ans = add(ans, f[i]);
    	}
    	printf("%d
    ", ans);
    }
    

    details

    竟然卡在怎么求方案数。。。降智严重。。。

    另外,本题是存在多项式解法的,详见xyx神仙的blog(虽然好像跑得没整数分拆快)

  • 相关阅读:
    Jquery使用live导致执行的内容会重复执行
    网上找的些tomact配置
    DatePicker 注意点 1.不用v-model 用:value 2.配合on-change进行回调 3.初始值 当天的用 (new Date()).toLocaleDateString().replace(///g, '-')
    Springboot框架 idea 工具 解决代码修改需要重新启动的方式
    合同签订
    ORACLE链接错误解决方案
    oracle中的split
    axis2和springboot冲突问题
    ActiveMQ中文乱码问题
    AXIS2的接口调用常见以及不常见问题
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13232495.html
Copyright © 2011-2022 走看看