@[TOC](INDEX)
CSP0 茉莉赛
说在前面
我也没有任何出题的必要。
就是手痒了想写的故事以及遇到了非常好的题。
我当然不会把故事留在这里啦。(因为没写好)
题面
T1
给一个数n,在数轴上找出一个长度>1的区间使得这个区间的所有数的异或和为n。
n
≤
1
0
18
,
T
≤
10
n\leq 10^{18},T\leq 10
n≤1018,T≤10。
输出这个区间的左右坐标。spj,但是因为我懒,所以这个坐标请让其
≤
1
0
18
\leq 10^{18}
≤1018,否则算你错,谢谢合作。
25分让你 Θ ( n 3 ) \Theta(n^3) Θ(n3)做,25分让你 Θ ( n 2 ) \Theta(n^2) Θ(n2)做,25分让你 Θ ( n 1 ) \Theta(n^1) Θ(n1)做,25分让你 Θ ( n 0 ) \Theta(n^0) Θ(n0)做。
T2
有一棵有根树,根是1,每个点有颜色,求出以每个点为根的子树中,出现次数最多的颜色的编号和。
比方说我的子树里面有5个1色,5个2色,5个3色,4个4色,2个5色,1个6色,1个7色,那我的答案就是1+2+3。
n
≤
1
0
5
n\leq 10^5
n≤105。
T3
两人取一堆n个石子,先手不能全部取完,之后每人取的个数不能超过另一个人上轮取的数*K。取完最后一个石子的人获胜。给n,K判断先手必胜并求第一步。
对
于
20
%
的
数
据
,
1
≤
k
≤
2
;
对于20\%的数据,1\leq k\leq 2;
对于20%的数据,1≤k≤2;
对
于
另
20
%
的
数
据
,
1
≤
k
≤
5
;
对于另20\%的数据,1\leq k\leq 5;
对于另20%的数据,1≤k≤5;
对
于
另
30
%
的
数
据
,
2
≤
n
≤
1000
;
对于另30\%的数据,2\leq n\leq 1000;
对于另30%的数据,2≤n≤1000;
对
于
100
%
的
数
据
,
5
≤
T
≤
10
,
2
≤
n
≤
100000000
,
1
≤
k
≤
100000
对于100\%的数据,5\leq T\leq 10,2\leq n\leq 100000000,1\leq k\leq 100000
对于100%的数据,5≤T≤10,2≤n≤100000000,1≤k≤100000
题解
T1
分析
原题UOJ 350 新年的XOR。
一点一点来。
我们可以枚举区间,然后暴力求异或和。
Θ
(
n
3
)
\Theta(n^3)
Θ(n3)。
我们可以求前缀异或和,于是枚举对于每个区间就可以
Θ
(
1
)
\Theta(1)
Θ(1)计算异或和了,
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)。
于是我们想到用个桶之类的东西,一边求前缀异或和,一边存对于每个异或前缀和值在哪个位置出现过了,如果
S
[
i
]
⊕
n
S[i]\oplus n
S[i]⊕n已经出现了,就找到了。
因为
a
⊕
b
=
c
⟺
a
⊕
c
=
b
a\oplus b=c \iff a\oplus c=b
a⊕b=c⟺a⊕c=b,而我们求前缀异或和(记
S
[
x
]
S[x]
S[x]为1~x的前缀异或和)
n
=
S
[
r
]
⊕
S
[
l
−
1
]
n=S[r]\oplus S[l-1]
n=S[r]⊕S[l−1]就
⟺
S
[
l
−
1
]
=
S
[
r
]
⊕
n
\iff S[l-1]=S[r]\oplus n
⟺S[l−1]=S[r]⊕n了。
于是现在优化到了
Θ
(
n
)
\Theta(n)
Θ(n)。
打了个表发现惊奇的规律,
{
n
%
4
=
0
时
,
S
[
n
]
=
n
。
n
%
4
=
1
时
,
S
[
n
]
=
1
。
n
%
4
=
2
时
,
S
[
n
]
=
n
+
1
。
n
%
4
=
3
时
,
S
[
n
]
=
0
。
\begin{cases}n\%4=0时,S[n]=n。\\ n\%4=1时,S[n]=1。\\ n\%4=2时,S[n]=n+1。\\n\%4=3时,S[n]=0。\end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧n%4=0时,S[n]=n。n%4=1时,S[n]=1。n%4=2时,S[n]=n+1。n%4=3时,S[n]=0。
这也非常容易证明,因为
(
2
k
)
⊕
(
2
k
+
1
)
=
1
(2k)\oplus(2k+1)=1
(2k)⊕(2k+1)=1,如果n是奇数,后面的若干对奇偶双双异或起来,都抵消成了1,总共
n
−
1
2
\frac{n-1}{2}
2n−1个,然后把这些个1和最前头的孤单的1异或起来,就得到了
S
[
n
]
=
n
+
1
2
个
1
的
异
或
和
=
n
+
1
2
&
1
S[n]=\frac{n+1}{2}个1的异或和=\frac{n+1}{2}\&1
S[n]=2n+1个1的异或和=2n+1&1;如果n是偶数,你既可以把后面的若干对奇偶双双异或起来再异或起1和2,也可以
S
[
n
]
=
S
[
n
−
1
]
⊕
n
S[n]=S[n-1]\oplus n
S[n]=S[n−1]⊕n,所以
S
[
n
]
=
n
+
(
n
+
1
2
&
1
)
S[n]=n+(\frac{n+1}{2}\&1)
S[n]=n+(2n+1&1)。
(去年的NOIp2030模拟赛用了这个结论但没有证明,看来我比去年强了一些。)
(因为去年是N合1,这个东西不是重点)
P.S.:
(
2
k
)
⊕
(
2
k
−
1
)
≠
1
(2k)\oplus(2k-1)\neq 1
(2k)⊕(2k−1)=1
S
[
5
]
=
1
⊕
2
⊕
3
⊕
4
⊕
5
=
1
⊕
(
2
⊕
3
)
⊕
(
4
⊕
5
)
=
1
⊕
1
⊕
1
=
1
S[5]=1\oplus 2\oplus 3\oplus 4\oplus 5=1\oplus (2\oplus 3)\oplus (4\oplus 5)=1\oplus1\oplus1=1
S[5]=1⊕2⊕3⊕4⊕5=1⊕(2⊕3)⊕(4⊕5)=1⊕1⊕1=1
S
[
6
]
=
S
[
5
]
⊕
6
=
1
⊕
2
⊕
3
⊕
4
⊕
5
⊕
6
=
1
⊕
(
2
⊕
3
)
⊕
(
4
⊕
5
)
⊕
6
=
1
⊕
6
=
7
S[6]=S[5]\oplus 6=1\oplus 2\oplus 3\oplus 4\oplus 5 \oplus 6=1\oplus (2\oplus 3)\oplus (4\oplus 5)\oplus 6=1\oplus 6=7
S[6]=S[5]⊕6=1⊕2⊕3⊕4⊕5⊕6=1⊕(2⊕3)⊕(4⊕5)⊕6=1⊕6=7
这个结论有什么用呢?
如果
n
%
4
=
0
n\%4=0
n%4=0,那就输出[1,n];
如果
n
%
4
=
2
n\%4=2
n%4=2,那就输出[2,n];
如果
n
%
4
=
3
n\%4=3
n%4=3,那就输出[1,n-1];
如果
n
%
4
=
1
n\%4=1
n%4=1,因为
S
[
n
−
1
]
=
n
−
1
=
S
[
n
−
4
]
⊕
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
1
⊕
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
S[n-1]=n-1=S[n-4]\oplus(n-3)\oplus(n-2)\oplus(n-1)=1\oplus(n-3)\oplus(n-2)\oplus(n-1)
S[n−1]=n−1=S[n−4]⊕(n−3)⊕(n−2)⊕(n−1)=1⊕(n−3)⊕(n−2)⊕(n−1),于是发现
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
n
(n-3)\oplus(n-2)\oplus(n-1)=n
(n−3)⊕(n−2)⊕(n−1)=n,所以输出[n-3,n-1]。
(
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
偶
⊕
奇
⊕
偶
=
奇
,
奇
⊕
1
=
奇
−
1
(n-3)\oplus(n-2)\oplus(n-1)=偶\oplus 奇\oplus偶=奇,奇\oplus 1=奇-1
(n−3)⊕(n−2)⊕(n−1)=偶⊕奇⊕偶=奇,奇⊕1=奇−1)。
然后对于0、1、2特判一下。
于是A了。
翻了翻排名前几(十)名的代码,发现他们都是对于n是奇数输出
[
n
−
3
,
n
−
1
]
[n-3,n-1]
[n−3,n−1],对于n是偶数输出
[
n
−
4
,
n
]
[n-4,n]
[n−4,n],然后特判0~4。
也是对的,挺好证明,可以按照n%4的余数分类,每一类就用上面的结论就好了。
代码
#include<cstdio>
#include<iostream>
using namespace std;
long long T,n;
int main()
{
cin>>T;
while(T--)
{
cin>>n;
if(n==1) puts("2 3");
else if(n==0) puts("1 3");
else if(n==2) puts("3 5");
else if(n%4==0) cout<<1<<" "<<n<<endl;
else if(n%4==2) cout<<2<<" "<<n<<endl;
else if(n%4==3) cout<<1<<" "<<n-1<<endl;
else cout<<n-3<<" "<<n-1<<endl;
}
}
T2
原题CF 600E。
并不复杂的DSU on tree,因为数据很宽松所以莫队啊LCT啊线段树合并啊主席树合并啊都可以过(也许吧没试过)。
何谓DSU on tree呢?
就这道题而言。
int mxcnt=0,sum=0,cnt[]={0};
//mxcnt代表出现最多次的颜色的数量,sum代表下标和,也就是答案,cnt是个桶,记录各个颜色它出现的次数
void update(int pos,int flg,int bad)//flg为1或-1,为1代表统计,即往桶里塞东西;为-1代表取东西。bad代表是否规避某个点。
{
cnt[col[pos]]+=flg;
if(flg>0 && cnt[col[pos]]==mxcnt) sum+=col[pos];
if(flg>0 && cnt[col[pos]]>mxcnt) mxcnt=cnt[col[pos]],sum=col[pos];
for(pos的所有儿子i) if(i!=bad) update(i,flg,bad);
}
void dfs(int pos,int flg)//flg代表是否要清除这个点的影响
{
for(all pos的轻儿子i) dfs(i,1);
if(pos有重儿子) dfs(pos的重儿子,0);
update(pos,1,pos的重儿子); //只统计轻儿子的答案
ans[pos]=sum;
if(!flg) update(pos,-1,0),sum=0,mxcnt=0;//如果是作为轻儿子被访问,将记录清零,当然此时谁也不需要规避
}
这个算法在干什么?
如果我们可以把以每个节点为根的子树的答案都分别存起来,这样我们就可以非常舒服的想怎么求答案就怎么求,可是空间开不下,我们只有一个答案数组,所以只能跑一次存一次清空一次。
有人提出:不用每次都清零!你只需要——
对于每个结点,先递归它的轻儿子,然后清空答案数组,然后递归它的重儿子,但不清空答案数组。所以现在我们回到这个结点的时候,就有了重儿子的信息。然后我们就再暴力统计轻儿子的信息,然后求得这个结点的答案。如果这个结点是重儿子的话,就什么也不做,如果是轻儿子的话,就清空答案数组。
什么是暴力统计?就是你把整颗子树都dfs遍历一遍,会做吧?
这里用撤回更合适,因为你再遍历一遍子树顶多是 Θ ( n ) \Theta(n) Θ(n)的,而且还能均摊;如果你memset,时间复杂度总是 Θ ( m a x n ) \Theta(maxn) Θ(maxn)的
为什么这么做是可行的?
……?
为什么这样做的效率有 Θ ( n log 2 n ) \Theta(n \log_2n) Θ(nlog2n)呢?
因为我们处理每个点的时候,都知道了重儿子的贡献,我们暴力处理轻儿子,因为轻儿子比较轻,所以时间复杂度就比较少,我不会证明重链剖分的时间复杂度(太懒了),我觉得这个时间复杂度的证明和重链剖分的时间复杂度的证明是一样的,所以请各位自行证明(臭不要脸)。
以下是代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline void Read(int &p)
{
p=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9')
p=p*10+c-'0',c=getchar();
}
const int MAXN=102030,MAXM=202030;
bool vis[MAXN];
int mx,col[MAXN];
long long sum,all[MAXN],ans[MAXN];
int n,u,v,cnt,head[MAXN],siz[MAXN],son[MAXN],to[MAXM],nxt[MAXM];
inline void addedge(int u,int v)
{
nxt[++cnt]=head[u],head[u]=cnt,to[cnt]=v;
}
void build(int pos)
{
vis[pos]=1,siz[pos]=1;
for(int i=head[pos];i;i=nxt[i])
if(!vis[to[i]])
{
build(to[i]),siz[pos]+=siz[to[i]];
if(siz[to[i]]>siz[son[pos]]) son[pos]=to[i];
}
}
void update(int pos,int flg,int rid)
{
all[col[pos]]+=flg;
if(flg>0)
{
if(all[col[pos]]==mx) sum+=col[pos];
if(all[col[pos]]>mx) sum=col[pos],mx=all[col[pos]];
}
for(int i=head[pos];i;i=nxt[i])
if(to[i]!=rid && siz[pos]>siz[to[i]]) update(to[i],flg,rid);
}
void dfs(int pos,int flg)
{
for(int i=head[pos];i;i=nxt[i])
if(to[i]!=son[pos] && siz[pos]>siz[to[i]]) dfs(to[i],1);
if(son[pos]) dfs(son[pos],0);
update(pos,1,son[pos]),ans[pos]=sum;
if(flg) update(pos,-1,0),sum=0,mx=0;
}
int main()
{
Read(n);
for(int i=1;i<=n;i++) Read(col[i]);
for(int i=2;i<=n;i++) Read(u),Read(v),addedge(u,v),addedge(v,u);
build(1),dfs(1,1);
for(int i=1;i<=n;i++) printf("%lld%s",ans[i],i==n?"\n":" ");
}
T3
原题为POJ 3922 、HDU 2486、HDU 2580,A simple stone game。
K=1
如果 K = 1 K=1 K=1,必败的答案为 2 n 2^n 2n。先手的必胜策略是取 n n n的最后一位 1 1 1,如 ( 25 ) 10 = ( 11001 ) 2 (25)_{10}=(11001)_2 (25)10=(11001)2就取走 1 1 1个石子, ( 24 ) 10 = ( 11000 ) 2 (24)_{10}=(11000)_2 (24)10=(11000)2,就取走 8 8 8个石头;这样对手无论如何取都会把若干个 0 0 0取为 1 1 1,然后你再取 n n n的最后一位。这样保证了始终是你把 1 1 1态变为 0 0 0态,对手把 0 0 0态变为 1 1 1态。周而复始,最后把石头取完的一定是你。
K=2
如果 K = 2 K=2 K=2,就是经典的斐波那契博弈问题。无解的答案为斐波那契数。
证明如下:
如果
n
n
n为斐波那契数
f
i
f_i
fi,考虑数学第二归纳法证明:
如果
i
=
3
,
f
i
=
2
i=3,f_i=2
i=3,fi=2,先手只能取一颗,显然必败;
假设对于
i
>
3
i\gt 3
i>3,
∀
3
≤
j
<
i
\forall ~ 3\leq j \lt i
∀ 3≤j<i都有
“
n
=
f
j
时
是
先
手
必
败
态
”
“n=f_j时是先手必败态”
“n=fj时是先手必败态”成立,现在只需证明
f
i
=
f
i
−
2
+
f
i
−
1
f_i=f_{i-2}+f_{i-1}
fi=fi−2+fi−1也是必败态。
稍微性感性一点的理解的话:
如果我先手取的石头数量比较大,比如说取了
≥
f
i
−
2
\geq f_{i-2}
≥fi−2颗石头,那么后手就会赢。
因为
f
i
−
1
<
2
∗
f
i
−
2
f_{i-1}<2*f_{i-2}
fi−1<2∗fi−2,剩下的石头至多是
f
i
−
1
f_{i-1}
fi−1颗,而这是先手2倍以内,可以被后手一步取完。
如果我先手取的石头比较少,比如取了
<
f
i
−
2
\lt f_{i-2}
<fi−2颗石头,那么后手还是会赢。
因为由我们之前的假设可知,对于前
f
i
−
2
f_{i-2}
fi−2的最后一颗石头(相当于一个
f
i
−
2
f_{i-2}
fi−2的子问题,而这已经在我们第二归纳法的假设中假设成立了)肯定可以由后手取到,此时留给我们先手的是
f
i
−
1
f_{i-1}
fi−1颗石头,而这也已经被假设为必败态了,先手
G
G
GG
GG了。
你说,当来到
f
i
−
1
f_{i-1}
fi−1颗石头的时候,因为上一局后手已经取过了,先手的取石子数有限制,这怎么办?
这不怎么办。必败态就是决定了你无论怎么取石头都会输的必败态,怎么挣扎都是
木大木大哒
\colorbox{#ffff00}{\color{RoyalBlue}{木大木大哒}}
木大木大哒的。
接下来证明所有非斐波那契数都是必胜态。
由齐肯多夫定理知,任意一个正整数都可以表示为若干个不相邻的斐波那契数之和(相邻的就可以直接合成为一个新的斐波那契数了,这样不断合成直至最后就可以不相邻了)。
设
n
=
f
a
1
+
f
a
2
+
⋯
+
f
a
k
(
假
设
是
从
小
到
大
排
序
)
n=f_{a_1}+f_{a_2}+\cdots+f_{a_k}(假设是从小到大排序)
n=fa1+fa2+⋯+fak(假设是从小到大排序),如
9
=
1
+
8
,
19
=
1
+
5
+
13
9=1+8,19=1+5+13
9=1+8,19=1+5+13。
那么我们先手先取走 f a 1 f_{a_1} fa1,因为不相邻,所以有 f a 2 > 2 ∗ f a 1 f_{a_2}>2*f_{a_1} fa2>2∗fa1,故后手无论如何取都不能把 f a 2 f_{a_2} fa2取完,于是这里又变成了一个 f a 2 f_{a_2} fa2的子问题,由上文知,这 f a 2 f_{a_2} fa2的最后一颗石头一定是我先手取到。同理,对于每个 f a i f_{a_i} fai的子问题,都是后手在面对,而最后一颗总是在我手上。
这样就可以拿到20分了。
仔细想想,发现K=1和K=2的情况是很类似的,二者的不同就在于一个是用
2
i
2^i
2i数列,一个是用斐波那契数列。
任意的K
对于
K
>
2
K\gt 2
K>2的情况。
由
K
=
2
K=2
K=2的情况得到启发,能不能把
n
n
n写成若干数的和,
n
=
a
1
+
a
2
+
⋯
+
a
k
n=a_1+a_2+\cdots+a_k
n=a1+a2+⋯+ak,进行类似的操作?
(即我取走
a
1
a_1
a1,就有:你不能一步取完
a
2
a_2
a2,并且经过若干步以后,
a
2
a_2
a2的最后一颗石头总是我的)
如果存在一种类似于 2 i 2^i 2i数列和斐波那契数列的数列 { a } = { a 1 , a 2 , a 3 , ⋯ } \{a\}=\{a_1,a_2,a_3,\cdots\} {a}={a1,a2,a3,⋯},使得任何正整数 n n n能用 { a } \{a\} {a}中的数求和得到,并且我们选中的数 a 1 + a 2 + ⋯ + a k ( 假 设 从 小 到 大 排 序 ) a_1+a_2+\cdots+a_k(假设从小到大排序) a1+a2+⋯+ak(假设从小到大排序)中, ∀ 1 ≤ i < k , 有 a i + 1 > K × a i \forall ~1\leq i\lt k,有a_{i+1}>K\times a_i ∀ 1≤i<k,有ai+1>K×ai,我们就可以用类似的方法:如果在这个数列中的数是必败态,而不在其中的,先手取最小的那一部分然后必胜,来判断是否能赢。
这个数列怎么找呢……
首先,我们记录一个数组
b
i
−
1
b_{i-1}
bi−1,代表只用前
i
−
1
i-1
i−1项能够构造出的最大的数。
比如
K
=
2
K=2~
K=2 时,
a
=
1
,
2
,
3
,
5
,
8
,
13
,
21
,
⋯
{a}={1,2,3,5,8,13,21,\cdots}
a=1,2,3,5,8,13,21,⋯,那么
b
3
b_3
b3代表只用
1
,
2
,
3
\bold {1,2,3}
1,2,3的斐波那契数能构造的最大数,即
4
=
1
+
3
4=1+3
4=1+3,而非
5
=
2
+
3
5=2+3
5=2+3,也不是
6
=
1
+
2
+
3
6=1+2+3
6=1+2+3,因为
5
=
5
5=5
5=5,
6
=
1
+
5
6=1+5
6=1+5。
不过当
K
>
2
K\gt 2
K>2时,我们不知道
5
5
5和
6
6
6是不是,我们可以令
b
3
=
6
b_3=6
b3=6,之后选
a
4
a_4
a4的时候不选5就是了。
然后再次使用归纳法。
假设已经找到了前
i
−
1
i-1
i−1项:
a
1
,
a
2
,
a
3
,
⋯
,
a
i
−
1
a_1,a_2,a_3,\cdots,a_{i-1}
a1,a2,a3,⋯,ai−1,也求出了
b
1
,
b
2
,
b
3
,
⋯
,
b
i
−
1
b_1,b_2,b_3,\cdots,b_{i-1}
b1,b2,b3,⋯,bi−1。因为我们要让任意自然数都能组合出来,但我们最大只能造出
b
i
−
1
b_{i-1}
bi−1,更大一点儿的数,比如
b
i
−
1
+
1
b_{i-1}+1
bi−1+1怎么组合出来呢?当然不能组合出来呢,
b
b
b数组已经决定了这一点。那么,为了把
b
i
−
1
+
1
b_{i-1}+1
bi−1+1组合出来,我们不得不把
b
i
−
1
+
1
b_{i-1}+1
bi−1+1加入
a
a
a数组中。
于是得到
a
i
=
b
i
−
1
+
1
a_i=b_{i-1}+1
ai=bi−1+1。
现在我们有了 a i a_i ai,考虑怎么求 b i b_i bi。因为我们构造出的 b i = a c 1 + a c 2 + ⋯ + a c k b_i=a{c_1}+a_{c_2}+\cdots+a_{c_k} bi=ac1+ac2+⋯+ack要满足 ∀ 1 ≤ z < k , 有 a c z + 1 > K × a c z \forall ~1\leq z\lt k,有a_{c_{z+1}}>K\times a_{c_z} ∀ 1≤z<k,有acz+1>K×acz。于是我们先选上 a i a_i ai,再往前找到第一个满足 a i > K × a t a_i>K\times a_t ai>K×at的t,于是就得到 b i = a i + b t b_i=a_i+b_t bi=ai+bt。
为什么要先选上
a
i
a_i
ai呢?
这是显然的……因为不用上
a
i
a_i
ai,你就是在求
b
i
−
1
b_{i-1}
bi−1……
然后选上了
a
i
a_i
ai以后就是一个简单的贪心……
为什么这样做能保证所有不在
{
a
}
\{a\}
{a}里的数都能被选中?
因为由于定义,
b
i
b_i
bi以前的所有数都可以被组合出来,而
b
i
b_i
bi以后是直接+1得到
a
i
+
1
a_{i+1}
ai+1,然后由于定义,
[
b
i
,
b
i
+
1
]
[b_i ,b_{i+1}]
[bi,bi+1]的数都可以组合出来。然后继续重复……所以只统计最大是可以关照到所有的数的……
关键问题是为什么这么定义就是对的?也就是问为什么
b
i
b_i
bi可以 并且
b
i
+
1
b_{i+1}
bi+1可以 就能保证
[
b
i
,
b
i
+
1
]
[b_i ,b_{i+1}]
[bi,bi+1]都可以?
emmm……荒废了一个小时以后发现我的能力不足以独立证明……
为什么能保证
n
=
b
i
+
t
,
1
≤
t
≤
b
i
+
1
−
b
i
n=b_i+t,1\leq t\leq b_{i+1}-b_{i}
n=bi+t,1≤t≤bi+1−bi能由不重复的
{
a
}
\{a\}
{a}组合出来呢?是分为
b
i
b_i
bi和
t
t
t两部分吗,但这又怎么保证不重复呢?
如果K=2时斐波那契数可以直接合并,那么
K
≠
2
K≠2
K=2时呢?
其实我们上述的操作已经类似于求证齐肯多夫定理了…………所以请参见广义齐肯多夫定理?
P.S.这篇博客就是求证了一遍齐肯多夫定理再拓展到广义齐肯多夫定理……
代码实现
于是你就把 { a } & { b } \{a\}\And \{b\} {a}&{b}手跑出来……然后看 n n n是不是其中一项……
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
void Read(int &p)
{
p=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9')
p=p*10+c-'0',c=getchar();
}
const int MAXN=2002030;
int t,n,k,a,b,id,ans,arr[MAXN],brr[MAXN];
int main()
{
Read(t);
while(t--)
{
id++;
Read(n),Read(k);
if(n<=k+1){printf("Case %d: lose\n",id);continue;}
a=0,b=0,arr[0]=brr[0]=1;
while(arr[a]<n)
{
a++,arr[a]=brr[a-1]+1;
while(arr[b+1]*k<arr[a]) b++;
brr[a]=arr[a]+(arr[b]*k<arr[a])*brr[b];
}
if(arr[a]==n) {printf("Case %d: lose\n",id);continue;}
while(n)
{
if(n>=arr[a]) ans=arr[a],n-=arr[a];
a--;
}
printf("Case %d: %d\n",id,ans);
}
}
说在后面
为什么你的博客里要有这么多无意义的显然的自问自答?
因为这是我在想的时候真的考虑到的东西,卡了好久发现啊真显然啊太没意义了啊……