标签:return Log int ll 选讲 deaf ans 杂题 dp
放个deaf语录
deaf:建议大家平时练习找题不要找太简单的,比如CF就找3000+,洛谷的话就选黑紫题就好了
注: 在座的有新初三的不到省一水平的同学。
[TJOI2019]甲苯先生的线段树
这题题面简直就是刻在DNA里的点分治(路径为某个数的路径条数)。
我们考虑借鉴点分治的思想,枚举 lca,然后在考虑两个子树各引出一条链拼接起来。
然而发现十分的不可做。
如果想使得 \(p\) 的左右儿子分别引出了一条长度为 \(i,j\) 的链(链分别从左儿子和右儿子开始算起),且这两条链加上 \(p\) 的编号和为 \(s\),那么注意到若 \(s,i,j\) 已知,我们可以分别讨论这两条链全往左儿子走和全往右儿子走的编号和,那么 \(s\) 一定在这两者之间。解一下发现 \(p\) 可以直接唯一确定。(目前还是没搞懂这玩意儿是怎么注意到的)。
剩下的就非常简单了。枚举 \(i,j\) 后考虑在每个深度往左和往右对答案的贡献,不难发现我们就是要用 \(2-1,2^2-1...2^{i-1}-1,2-1,2^2-1...2^{j-1}-1\) 凑出一个数。枚举用了多少个数转化为数位 dp 模板就行了。
复杂度 \(O(n^5)\),但上界非常松,跑得飞快。
#include <cstdio>
#include <algorithm>
#include <cstring>
inline int min(const int x, const int y) {return x < y ? x : y;}
inline int max(const int x, const int y) {return x > y ? x : y;}
typedef long long ll;
int Log(ll n) {
int x = 0;
while (n != 1) n >>= 1, ++ x;
return x;
}
ll getdis(ll a, ll b) {
if (Log(a) < Log(b)) a ^= b ^= a ^= b;
int tmp = Log(a) - Log(b);
ll ans = 0;
while (tmp --) ans += a, a >>= 1;
if (a == b) return ans + a;
while ((a >> 1) != (b >> 1)) ans += a + b, a >>= 1, b >>= 1;
return ans + a + b + (a >> 1);
}
ll dp[55][105][2], c[105];
ll solve(ll n, int a, int b) {
if (!n) return 1;
if (a < 0) a = 0;
if (b < 0) b = 0;
memset(c, 0, sizeof c);
for (int i = 1; i <= a; ++ i) ++ c[i];
for (int i = 1; i <= b; ++ i) ++ c[i];
ll ans = 0;
for (int k = 0; k <= a + b; ++ k) if (!(n + k & 1ll)) {
int lim = Log(n + k);
dp[0][0][0] = 1;
for (int i = 1; i <= lim; ++ i)
for (int j = 0; j <= 2 * i && j <= a + b; ++ j) {
dp[i][j][0] = dp[i][j][1] = 0;
if (n + k & 1ll << i) {
dp[i][j][0] = dp[i - 1][j][1];
if (c[i] && j >= 1) dp[i][j][0] += dp[i - 1][j - 1][0] * c[i];
if (c[i] == 2 && j >= 2) dp[i][j][1] = dp[i - 1][j - 2][1];
} else {
dp[i][j][0] = dp[i - 1][j][0];
if (c[i] && j >= 1) dp[i][j][1] = dp[i - 1][j - 1][1] * c[i];
if (c[i] >= 2 && j >= 2) dp[i][j][1] += dp[i - 1][j - 2][0];
}
}
ans += dp[lim][k][0];
}
return ans;
}
int main() {
int _;
scanf("%d", &_);
while (_ --) {
int n, type;
ll a, b, s = 0;
scanf("%d%lld%lld%d", &n, &a, &b, &type);
s = getdis(a, b);
if (type == 1) {printf("%lld\n", s); continue;}
ll ans = 0;
for (int i = 0; i < n; ++ i)
for (int j = 0; j < n; ++ j) {
ll p = (s + 1 - (1ll << j)) / ((1ll << i + 1) + (1ll << j + 1) - 3);
if (p <= 0) break;
if (Log(p) + max(i, j) >= n) continue;
if (((1ll << i + 1) + (1ll << j + 1) - 3) * p + (1ll << j + 1) - 3 - i - j + (1ll << i) < s) continue;
ans += solve(s - ((1ll << i + 1) + (1ll << j + 1) - 3) * p - (1ll << j) + 1, i - 1, j - 1);
}
printf("%lld\n", ans - 1);
}
}
标签:return,Log,int,ll,选讲,deaf,ans,杂题,dp 来源: https://www.cnblogs.com/stinger/p/16537960.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。