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;
}
posted @ 2021-11-09 20:50  mrclr  阅读(44)  评论(0编辑  收藏  举报