题目描述
给定 TTT 个数 n1,n2,⋯ ,nTn_{1}, n_{2}, \cdots, n_{T}n1,n2,⋯,nT, 对每个 nin_{i}ni 请求出有多少组 a,b,ca, b, ca,b,c 满足:
-
1≤a,b,c≤ni1 \leq a, b, c \leq n_{i}1≤a,b,c≤ni;
-
a⊕b⊕c=0a \oplus b \oplus c=0a⊕b⊕c=0 ,其中 ⊕\oplus⊕ 表示二进制按位异或;
-
长度为 a,b,ca, b, ca,b,c 的三条边能组成一个三角形。
输入格式
输入的第一行包含一个整数 TTT 。
接下来 TTT 行每行一个整数, 分别表示 n1,n2,⋯ ,nTn_{1}, n_{2}, \cdots, n_{T}n1,n2,⋯,nT 。
输出格式
输出 TTT 行, 每行包含一个整数, 表示对应的答案。
输入输出样例 #1
输入 #1
2
6
114514
输出 #1
6
11223848130
说明/提示
对于 10%10 \%10% 的评测用例, T=1,1≤ni≤200T=1,1 \leq n_{i} \leq 200T=1,1≤ni≤200; 对于 20%20 \%20% 的评测用例, T=1,1≤ni≤2000T=1,1 \leq n_{i} \leq 2000T=1,1≤ni≤2000 ;
对于 50%50 \%50% 的评测用例, T=1,1≤ni≤220T=1,1 \leq n_{i} \leq 2^{20}T=1,1≤ni≤220;
对于 60%60 \%60% 的评测用例, 1≤T≤100000,1≤ni≤2201 \leq T \leq 100000,1 \leq n_{i} \leq 2^{20}1≤T≤100000,1≤ni≤220;
对于所有评测用例, 1≤T≤100000,1≤ni≤2301 \leq T \leq 100000,1 \leq n_{i} \leq 2^{30}1≤T≤100000,1≤ni≤230 。
蓝桥杯 2021 国赛 A 组 I 题(B 组 J 题)。
我的题解
现有的题解简洁又优美,状态转移方程清晰易懂。
相比之下,我的做题思路复杂,程序丑陋又效率低下。
下面简单讲讲我的思路。
计算有多少三元组 (a,b,c)(a,b,c)(a,b,c) 满足
- a>b>ca>b>ca>b>c ;
- a=b⊕ca=b \oplus ca=b⊕c ;
- a<b+ca<b + ca<b+c .
然后再对结果乘以6。
不难发现,满足上列条件的三元组具有以下特征——
- 设 aaa 的最高位为 lenlenlen,则 alen=1,blen=1,clen=0a_{len}=1,b_{len}=1,c_{len}=0alen=1,blen=1,clen=0
- 存在 pospospos,使得 apos=1,bpos=0,cpos=1a_{pos}=1,b_{pos}=0,c_{pos}=1apos=1,bpos=0,cpos=1
- posmax=max{pos}pos_{\max}=\max\{pos\}posmax=max{pos} 使得 apos=1,bpos=0,cpos=1a_{pos}=1,b_{pos}=0,c_{pos}=1apos=1,bpos=0,cpos=1
- 对任意 posmax<i<lenpos_{\max}<i<lenposmax<i<len,有 ai=bia_i=b_iai=bi
- 存在 k<posmaxk<pos_{\max}k<posmax,使得 ak=0,bk=1,ck=1a_{k}=0,b_{k}=1,c_{k}=1ak=0,bk=1,ck=1
考虑枚举 posmaxpos_{\max}posmax,若对答案产生贡献,则
-
存在 i>posmaxi>pos_{\max}i>posmax,使得 ai=1a_i=1ai=1 ;若有 numnumnum 个自由空位,系数贡献为 2num−12^{num}-12num−1
-
存在 i<posmaxi<pos_{\max}i<posmax,使得 ai=0a_i=0ai=0 ;若有 numnumnum 个自由空位,系数贡献为 fnum=∑i=1num(2i−1)∗2num−j∗Cnumif_{num}=\sum_{i=1}^{num} (2^i-1)*2^{num-j}*C_{num}^ifnum=∑i=1num(2i−1)∗2num−j∗Cnumi ( iii 表示0的个数)
先考虑 aaa 最高位 lenlenlen 取0,从1到 len−2len-2len−2 枚举 posmaxpos_{\max}posmax 即可。
再考虑 aaa 最高位 lenlenlen 取1的情况,从1到 len−1len-1len−1 枚举 posmaxpos_{\max}posmax ;
非边界情况高位系数很好算,即截取 len−1len-1len−1 到 posmax+1pos_{\max}+1posmax+1 转为十进制,低位系数为 fposmaxf_{pos_{\max}}fposmax 。
边界情况需特殊处理,来个具体数字作为例子, N=(11010101)2N=(11010101)_2N=(11010101)2
假设现在 posmax=6pos_{\max}=6posmax=6,即求解 aaa 的最低6位为 000000⋯010101000000 \cdots 010101000000⋯010101 时,存在 k<posmaxk<pos_{\max}k<posmax,使得 ak=0,bk=1,ck=1a_{k}=0,b_{k}=1,c_{k}=1ak=0,bk=1,ck=1 分解法数量。
从高到低对1进行处理,转化为 000000⋯001111000000 \cdots 001111000000⋯001111、010000⋯010011010000 \cdots 010011010000⋯010011、010100010100010100、010101010101010101 四个部分。
再分别考虑k是否在后缀处,进行计算。
如果k不在后缀处,根据更高位的01数量计算左侧系数 (2num0−1)∗2num1(2^{num_0}-1)*2^{num_1}(2num0−1)∗2num1,后缀处01可任取,如果有 numnumnum 个后缀连续1,右侧系数为 4num4^{num}4num.
如果k在后缀处,根据更高位的1的数量计算左侧系数 2num12^{num_1}2num1;如果有 numnumnum 个后缀连续1,右侧系数为 fnumf_{num}fnum.
代码:
typedef long long ll;
#include<bits/stdc++.h>
using namespace std;
int fj[35];
ll ycl[35],yh[35][35],Ans[35];
ll work0(int pos)
{
ll ans=0;
int lst1=-1,zero=0,one=0;
while (~pos)
{
if (fj[pos])
{
ans+=((1ll<<zero+1)-1)*(1ll<<one)*(1ll<<pos)*(1ll<<pos);
ans+=(1ll<<one)*ycl[pos];
one++;
}
else
zero++;
pos--;
}
ans+=((1ll<<zero)-1)*(1ll<<one);
return ans;
}
void work(int N)
{
int len=-1;
while (N) fj[++len]=N%2,N/=2;
ll ans=Ans[len];
int now=0;
for (int i=len-1;~i;i--)
{
ans+=ycl[i]*now;
if (fj[i])
ans+=work0(i-1);
now=now*2+fj[i];
}
ans*=6;
cout<<ans<<endl;
}
void pre()
{
yh[0][0]=1;
for (int i=1;i<=30;i++)
{
yh[i][0]=1;
for (int j=1;j<=i;j++)
yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
}
for (int i=1;i<=30;i++)
{
for (int j=1;j<=i;j++)
ycl[i]+=((1ll<<j)-1)*(1ll<<(i-j))*yh[i][j];
}
ll ans;
for (int len=0;len<=30;len++)
{
ans=0;
for (int i=0;i<len;i++)
{
int j=len-1-i;
ans+=ycl[i]*((1ll<<j)-1);
}
Ans[len]=ans;
}
}
int main()
{
pre();
int T;cin>>T;
for (int i=1;i<=T;i++)
{
int N;cin>>N;
work(N);
}
return 0;
}
时间复杂度
O(T(logN)2)\mathcal{O}(T(\log N)^2)O(T(logN)2)
我的方法实在是太复杂了,时间复杂度也不优秀。
还是希望大家多学学那些优美的数位dp。

被折叠的 条评论
为什么被折叠?



