zoukankan      html  css  js  c++  java
  • AtCoder AGC031B Reversi (DP计数)

    简单的计数题。(总算做出一道AGC的B题了,然而这场比赛我忘记打了233333)

    题目链接: https://atcoder.jp/contests/agc031/tasks/agc031_b

    题意: 有一个长度为(N)的颜色序列,第(i)个位置初始颜色为(a_i), 可以执行若干次操作,每次可以选择两个颜色一样的位置,然后把这两个位置中间的区间都刷成和两端相同的颜色,问最后本质不同的序列有多少种。

    题解: 最重要的想法就是要深刻地理解本质不同。

    因为我们不论如何操作,最后得到的序列一样就算一样,所以假设([l_1,r_1])([l_2,r_2])分别是先后两次操作。

    若前者和后者相交但不包含,若前后两次刷成的颜色相同,我们可以等效成一次操作,操作区间为它们的并。(等效法?文化课走火入魔了吧)若前后两次颜色不同,那么这种情况一定是不存在的,因为相交但不包含意味着第一个区间的一个端点在第二个区间里,那这个端点在执行完前面的操作之后就不再是颜色2而变成颜色1了。

    若前者包含后者,则后者无用。

    若后者包含前者,则前者无用。

    若两次区间不相交,则两次都有用。

    所以本题就是要求把长度为(N)的序列内取出若干不相交区间,每个区间两端点颜色相同的方案数。

    我们先对序列进行如下处理: 把所有连续的颜色相同的区间缩成一个位置。例如122333441变成12341.

    然后(f_i)表示前(i)个的方案数。

    (f_i=sum_{jle i, a_j=a_i} f_{j-1})

    桶优化。

    时间复杂度(O(n)).

    特发此文,假装自己还没AFO。

    代码

    好长啊,500多B.

    
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #define llong long long
    using namespace std;
    
    const int N = 2e5;
    const int P = 1e9+7;
    int a[N+3],b[N+3];
    llong f[N+3],g[N+3];
    int n;
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    	int m = 0; for(int i=1; i<=n; i++) if(i==0 || a[i]!=a[i-1]) {m++; b[m] = a[i];}
    	n = m; for(int i=1; i<=n; i++) a[i] = b[i];
    	f[0] = 1ll; g[a[1]] = 1ll;
    	for(int i=1; i<=n; i++)
    	{
    		f[i] = g[a[i]];
    		g[a[i+1]] = (g[a[i+1]]+f[i])%P;
    	}
    	printf("%lld
    ",f[n]);
    	return 0;
    }
    
    
  • 相关阅读:
    mysql 开发进阶篇系列 18 MySQL Server(innodb_buffer_pool_size)
    sql server 备份与恢复系列三 简单恢复模式下的备份与还原
    (后端)SQL Server日期时间函数
    (网页)the server responded with a status of 403 (Forbidden)
    (其他)小程序介绍和开发
    (网页)jQuery UI 实例
    (其他)令人忧虑,不阅读的中国人
    (其他)程序员的8种好习惯
    (后端)NoSuchMethodError
    JsonParseException:非法的unquoted字符((CTRL-CHAR,代码9)):必须被转义
  • 原文地址:https://www.cnblogs.com/suncongbo/p/10545055.html
Copyright © 2011-2022 走看看