CF1905E One-X题解
CF1905E One-X
思路详情:
首先读题发现:可以正难则反,即去枚举集合的LCA,也就是从上向下遍历。
然后考虑对于以 \(id\) 为根的子树的贡献,设其为 \(dp_{id}\),设以 \(id\) 为根的子树为区间 \(l\) 到 \(r\) ,长度为 \(len_{id}\) ,我们可以发现其左子树的 \(siz_{lid}\) 为 \((len + 1) / 2\) ,右子树的 \(siz_{rid}\) 为 \(len / 2\) 。
则节点 \(id\) 的贡献为 \((2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\) (题目中给的集合数公式 \(2 ^ n - 1\) )。
以 \(id\) 为根的子树的贡献为 \(dp_{lid} + dp_{rid} + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\) 。
就有了转移方程 \(dp_{id} = dp_{lid} + dp_{rid} + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\) 。
于是我们就能得到 19tps 的暴力代码:
#include<bits/stdc++.h>
#define int long long
#define putchar_unlocked _putchar_nolock
#define getchar_unlocked _getchar_nolock
#define ent putchar_unlocked('\n')
#define con putchar_unlocked(' ')
#define Blue_Archive return 0
using namespace std;
const int N = 65;
const int mod = 998244353;
const int inv = 499122177;
int T;
int n;
int ans;
inline int read()
{
int k = 0,f = 1;
char c = getchar_unlocked();
while(c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar_unlocked();
}
while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();
return k * f;
}
inline void write(int x)
{
if(x < 0) putchar_unlocked('-'),x = -x;
if(x > 9) write(x / 10);
putchar_unlocked(x % 10 + '0');
}
inline int qpow(int a,int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
//容斥一下:res = (2 ^ (siz[ls] * siz[rs]) - 1) * val = (2 ^ siz[id] - 1 - (2 ^ siz[ls] - 1) - (2 ^ siz[rs] - 1)) * val = (qpow(2,len) - qpow(2,len / 2) - qpow(2,(len + 1) / 2) + 1) * val
inline int build(int l,int r,int val,int id)
{
if(l == r) return val;
int len = r - l + 1,mid = (l + r) >> 1;
int res = ((qpow(2,len / 2) - 1) * (qpow(2,(len + 1) / 2) - 1) % mod * val) % mod;
if(len % 2 == 1) return ((res + build(l,mid,2 * val,id)) % mod + build(mid + 1,r,2 * val + id,id)) % mod;
else return (res + build(l,mid,4 * val + id,id * 2)) % mod;
}
signed main()
{
// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
T = read();
while(T --)
{
n = read();
ans = build(1,n,1,1);
write((ans % mod + mod) % mod);ent;
}
Blue_Archive;
}
之后神秘的来了,我们发现这个柿子里的 \(id\) 很恶心,貌似可以维护一下。
考虑更优的时间复杂度,发现如果将搜索改成记忆化搜索的话将会获得严格 \(O(log_{n})\) 的时间复杂度,可以通过此题。
于是就考虑搞一个跟 \(id\) 有关的柿子。
发现貌似是一个一次函数。
设 \(dp_{id}\) 为 \(k_{id} * id + b_{id}\) ,发现 \(k_{id}\) 和 \(b_{id}\) 都能从 \(k_{lid}\) 、 \(k_{rid}\) 、 \(b_{lid}\) 、 \(b_{rid}\) 转移过来。
所以就可以转移了。
\(dp_{id} = dp_{lid} + dp_{rid} + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\)
\(k_{id} * id + b_{id} = k_{lid} * lid + b_{lid} + k_{rid} * rid + b_{rid} + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\)
\(k_{id} * id + b_{id} = k_{lid} * id * 2 + b_{lid} + k_{rid} * (id * 2 + 1) + b_{rid} + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id\)
\(k_{id} * id + b_{id} = k_{lid} * id * 2 + k_{rid} * id * 2 + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1) * id + k_{rid} + b_{rid} + b_{lid}\)
\(k_{id} * id + b_{id} = (k_{lid} * 2 + k_{rid} * 2 + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1)) * id + k_{rid} + b_{rid} + b_{lid}\)
所以可得:
\(k_{id} = k_{lid} * 2 + k_{rid} * 2 + (2 ^ {siz_{lid}} - 1) * (2 ^ {siz_{rid}} - 1)\)
\(b_{id} = k_{rid} + b_{rid} + b_{lid}\)
最后答案就为以 \(1\) 为根的子树的贡献,即为 \(dp_i = dp_k * 1 + dp_b = dp_k + dp_b\) 。
然后我们就可以愉快地 A 掉这道题了。
tips:因为 \(id\) 特别大,开数组开不下,有因为是记忆化搜索,不需要存很多东西,所以空间复杂度可以保证,至于怎么开嘛,用 \(map\) 就行了。
#include<bits/stdc++.h>
#define int long long
#define lid (id << 1)
#define rid (id << 1 | 1)
#define putchar_unlocked _putchar_nolock
#define getchar_unlocked _getchar_nolock
#define ent putchar_unlocked('\n')
#define con putchar_unlocked(' ')
#define Blue_Archive return 0
using namespace std;
const int mod = 998244353;
int T;
int n;
struct miku
{
int k;
int b;
}ans;
map<int,miku> mp;
inline int read()
{
int k = 0,f = 1;
char c = getchar_unlocked();
while(c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar_unlocked();
}
while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();
return k * f;
}
inline void write(int x)
{
if(x < 0) putchar_unlocked('-'),x = -x;
if(x > 9) write(x / 10);
putchar_unlocked(x % 10 + '0');
}
inline int qpow(int a,int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
inline miku build(int id,int len)
{
if(mp.count(len)) return mp[len];
if(len == 1) return mp[len] = (miku){1,0};
if(len == 0) return mp[len] = (miku){0,0};
int x = (len + 1) / 2,y = len / 2;
miku tx = build(lid,x);
miku ty = build(rid,y);
miku res = (miku){(tx.k * 2 % mod + ty.k * 2 % mod) % mod + (qpow(2,x) - 1) * (qpow(2,y) - 1) % mod,(tx.b + ty.b + ty.k) % mod};
return mp[len] = res;
}
signed main()
{
// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
T = read();
while(T --)
{
n = read();
mp.clear();
ans = build(1,n);
write((ans.k + ans.b) % mod);ent;
}
Blue_Archive;
}

浙公网安备 33010602011771号