• CCPC2019 Harbin Site B.Binary Numbers


    传送门


    题面挺不好懂,简单来说就是有\(0 \sim 2^m-1\)个数被划分成了\(n\)段,从每一段中选出一个数\(a_i\),要满足题中的限制。而一种\(\{a_i\}\)选择方案的价值是这\(n\)个数的乘积,问所有合法的方案的价值之和对\(100000007\)取模的结果。


    首先关于题目中的函数\(F_m(a,b)\),其实就是\(a,b\)二进制表示下的最长公共前缀(把最高位看成第一位)。而这个函数实际上是一个单峰函数,即对于\(F_m(a,b)\)来说,当\(b < a\)时,\(b\)越小,\(LCP(a,b)\)越短;当\(b > a\)时,\(b\)越大,\(LCP(a,b)\)越小。

    那么如果要满足题中的限制,对于每个区间需要满足:

    1. \(LCP(a_i,l_i)\geqslant LCP(a_{i-1},l_i)\),
    2. \(LCP(a_i,r_i)\geqslant LCP(a_{i+1},r_i)\).

    可以采用动态规划,状态还是比较巧妙的:令\(dp[i][j][k]\)表示到第\(i\)个区间,\(LCP(a_i,l_{i+1})=j,LCP(a_i,r_i)=k\)时的贡献和。枚举\(a_i\),考虑如何从\(dp[i-1]\)转移到\(dp[i]\):记上一层的状态为\(dp[i-1][j_{lst}][k_{lst}]\),这一维的状态为\(dp[i][j_{now}][k_{now}]\),那么\(j_{lst} \in [0,LCP(a_i,l_i)],k_{lst} \in [LCP(a_i,r_{i-1}),m]\),这个区间中的\(j_{lst}\)\(k_{lst}\)都可以转移到\(j_{now}\)\(k_{now}\),乘上位权\(a_i\)即可.

    这样时间复杂度是\(O(2^m m^2)\).

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const ll mod = 100000007;
    const int maxn = (1 << 17) + 5;
    const int maxm = 18;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    In ll ADD(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;}
    
    int n, m, l[maxn], r[maxn];
    
    In int lcp(int a, int b)
    {
    	for(int i = m - 1; i >= 0; --i)
    		if(((a >> i) & 1) ^ ((b >> i) & 1)) return m - i;
    	return m;
    }
    
    ll dp[maxn][maxm][maxm];
    In ll solve()
    {
    	for(int i = 0; i <= n; ++i)
    		for(int j = 0; j <= m; ++j) fill(dp[i][j], dp[i][j] + m + 1, 0);
    	dp[0][0][m] = 1;
    	for(int i = 1; i <= n; ++i)
    	{
    		for(int x = l[i]; x <= r[i]; ++x)
    		{
    			int lstl = lcp(x, l[i]), lstr = lcp(x, r[i - 1]);
    			int nowl = i == n ? 0 : lcp(x, l[i + 1]);
    			int nowr = lcp(x, r[i]);
    			for(int j = 0; j <= lstl; ++j)
    				for(int k = lstr; k <= m; ++k)
    					dp[i][nowl][nowr] = ADD(dp[i][nowl][nowr], dp[i - 1][j][k] * x % mod);
    		}
    	}
    	ll ans = 0;
    	for(int j = 0; j <= m; ++j)
    		for(int k = 0; k <= m; ++k) ans = ADD(ans, dp[n][j][k]);
    	return ans;
    }
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		m = read(), n = read();
    		for(int i = 1; i <= n; ++i) l[i] = read(), r[i] = read();
    		write(solve()), enter;
    	}
    	return 0;
    }
    
  • 相关阅读:
    react深入学习(资料,案例)
    match.exec深入学习
    实用插件表格行列隐藏显示
    下拉选项插件的实现
    表格操作eventTable
    [CentOS7] 挂载iso镜像文件到/media目录下
    [CentOS7] 设置开机启动方式(图形界面或命令行)
    [CentOS7] 磁盘分区(gdisk, fdisk)
    [CentOS7] minimal安装后 出现 没有ifconfig 无法ping 无法yum could not retrieve mirrorlist http://mirrorlist.centos.org/
    [C++]C,C++中使用可变参数
  • 原文地址:https://www.cnblogs.com/mrclr/p/15530878.html
Copyright © 2020-2023  润新知