[TJOI2019]唱、跳、rap和篮球
暴力枚举有多少个不满足条件的,有
i
i
i 个不满足的情况相当于在
n
−
3
∗
i
n-3*i
n−3∗i 个里面选
i
i
i 个
其它的随便填,大力容斥,
N
T
T
NTT
NTT即可
a
n
s
=
∑
i
=
0
n
/
4
(
−
1
)
i
(
n
−
3
∗
i
i
)
(
n
−
i
∗
4
)
!
∑
x
1
+
x
2
+
x
3
+
x
4
=
n
−
4
∗
i
1
x
1
!
1
x
2
!
1
x
3
!
1
x
4
!
ans=\sum_{i=0}^{n/4}(-1)^i\binom{n-3*i}{i}(n-i*4)!\sum_{x_1+x_2+x_3+x_4=n-4*i}\frac{1}{x_1!}\frac{1}{x_2!}\frac{1}{x_3!}\frac{1}{x_4!}
ans=i=0∑n/4(−1)i(in−3∗i)(n−i∗4)!x1+x2+x3+x4=n−4∗i∑x1!1x2!1x3!1x4!1
[TJOI2019]甲苯先生的滚榜
可以直接平衡树,每次修改就暴力删除再添加
也可以用树状数组维护严格小于它的个数,在对每个点动态开点一棵线段树维护第二维大于它的
[TJOI2019]甲苯先生的字符串
f
i
,
j
=
∑
k
f
i
−
1
,
k
[
c
a
n
(
k
j
)
]
f_{i,j}=\sum_k f_{i-1,k}[can(kj)]
fi,j=∑kfi−1,k[can(kj)]
a
i
,
j
a_{i,j}
ai,j 表示字符
i
,
j
i,j
i,j 能不能放在一起,然后就是一个裸的矩阵乘
[TJOI2019]大中锋的游乐场
建好分层图后就是裸的最短路
[TJOI2019]甲苯先生和大中锋的字符串
考虑到一个串的出现次数就是
∣
R
i
g
h
t
∣
|Right|
∣Right∣,而该节点包涵的串的长度为
(
l
e
n
[
l
i
n
k
[
i
]
]
,
l
e
n
[
i
]
]
(len[link[i]],len[i]]
(len[link[i]],len[i]]
差分打个标记即可
[TJOI2019]甲苯先生的线段树
先考虑第一问,
l
c
a
lca
lca 就是两点的
l
c
p
lcp
lcp
然后考虑求一个点到根的和,对于每一位求一下就是
2
∗
x
−
p
o
p
c
n
t
(
x
)
2*x - popcnt(x)
2∗x−popcnt(x)
然后差分就是答案
第二问,考虑凭借运气枚举一些东西得到答案
如果直觉够准的话,你开始枚举一个点两端的长度
对于一个点,如果它左边长度为
a
a
a,右边长度为
b
b
b,那么
l
c
a
lca
lca 的贡献就是
l
+
2
l
+
.
.
.
+
2
a
l
=
(
2
a
+
1
−
1
)
l
l+2l+...+2^al=(2^{a+1}-1)l
l+2l+...+2al=(2a+1−1)l,所以就是
(
2
a
+
1
+
2
b
+
1
−
3
)
l
(2^{a+1}+2^{b+1}-3)l
(2a+1+2b+1−3)l
如果全部走左边,最小贡献为全部往右走,贡献为
1
+
2
+
.
.
.
+
2
b
−
1
=
2
b
−
1
1+2+...+2^{b-1}=2^b-1
1+2+...+2b−1=2b−1
最大贡献为
2
b
+
1
+
2
a
−
a
−
b
−
3
2^{b+1}+2^a-a-b-3
2b+1+2a−a−b−3
增量的最大值
2
b
+
1
+
2
a
−
a
−
b
−
3
2^{b+1}+2^a-a-b-3
2b+1+2a−a−b−3 都比
(
2
a
+
1
+
2
b
+
1
−
3
)
(2^{a+1}+2^{b+1}-3)
(2a+1+2b+1−3)小
也就是一个
l
c
a
lca
lca 加上增量的最大值都比另一个
l
c
a
lca
lca 的最小值小
于是
l
c
a
lca
lca 可以定下来,为
s
2
a
+
1
+
2
b
+
1
−
3
\frac{s}{2^{a+1}+2^{b+1}-3}
2a+1+2b+1−3s
然后令
k
=
s
%
(
2
a
+
1
+
2
b
+
1
−
3
)
k=s\%({2^{a+1}+2^{b+1}-3})
k=s%(2a+1+2b+1−3)
要使
a
+
b
−
c
n
t
(
a
)
−
c
n
t
(
b
)
=
k
a+b-cnt(a)-cnt(b)=k
a+b−cnt(a)−cnt(b)=k
枚举
c
n
t
(
a
)
+
c
n
t
(
b
)
cnt(a)+cnt(b)
cnt(a)+cnt(b)
也就是选两个二进制数使其和为 k,
p
o
p
c
n
t
popcnt
popcnt 之和为一个定值的方案数
方案数可以用数位dp 搞出来
f
i
,
j
,
0
/
1
f_{i,j,0/1}
fi,j,0/1 表示到第 i 位,填了 j 个1,有没有进位到 i+1 的方案数
枚举当前
a
a
a填1,
b
b
b 填1,都填,都不填转移就可以了
虽然很不好想,但是这种化简问题的方法可以学习
通过一步步转换,我们竟然将答案转换成了使
a
+
b
=
x
a+b=x
a+b=x 的方案数
值得学习!
#include<bits/stdc++.h>
#define N 55
using namespace std;
typedef long long ll;
ll read(){
ll cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch=='-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int T, d, typ; ll s, t;
ll dp[2][N][N<<1];
ll solve(ll sum, ll cnt, ll b1, ll b2){
ll mxb = 1; while((1ll << mxb) <= sum) ++mxb;
for(int i = 0; i <= cnt; i++) dp[0][0][i] = dp[1][0][i] = 0;
dp[0][0][0] = 1;
for(int b = 1; b < mxb; b++){
sum >>= 1;
for(int c = 0; c <= cnt; c++){
dp[0][b][c] = dp[sum&1][b-1][c];
dp[1][b][c] = 0;
if(c && b < b1) dp[~sum&1][b][c] += dp[~sum&1][b-1][c-1];
if(c && b < b2) dp[~sum&1][b][c] += dp[~sum&1][b-1][c-1];
if(c && b < b1 && b < b2) dp[1][b][c] += dp[sum&1][b-1][c-2];
}
} return dp[0][mxb-1][cnt];
}
ll lca(ll x, ll y){ while(x^y){ if(x<y) swap(x,y); x >>= 1;} return x;}
ll pc(ll x){ ll ret = 0; while(x) x -= x&-x, ++ret; return ret;}
ll calc(ll x){ return (x << 1) - pc(x);}
int main(){
T = read();
while(T--){
d = read(), s = read(), t = read(), typ = read();
ll p = lca(s, t); ll sum = calc(s) + calc(t) - calc(p) - calc(p >> 1);
if(typ == 1){ cout << sum << '\n'; continue;}
ll ans = 0;
for(int i = 0; i < d; i++){
for(int j = 0; j < d; j++){
ll ss = (1ll << i + 1) + (1ll << j + 1) - 3; if(sum < ss) break;
ll pos = sum / ss; int l = 0; while(pos){ pos >>= 1; ++l; }
if(l + i > d || l + j > d) continue;
ll k = sum % ss - (1ll << j) + 1;
if(k < 0) continue;
for(int t = k&1; t <= i+j; t+=2)
ans += solve(k + t, t, i, j);
}
} cout << ans - 1 << '\n';
} return 0;
}