BZOJ Dec. Monthly

##A 彩虹溜冰鞋

有一个 $R\times C$ 的循环网格地图(即走出上边界的上方为下边界,以此类推),给定一个起点 $(I,J)$ 。在上面走 $N$ 步,初始时面向正上方,走第 $i$ 步时向前走 $i$ 步,然后顺时针转 $90^\circ$ 。初始时颜色为 ‘ A ‘ ,之后没一步的颜色循环地分配下一个字母( ‘ Z ‘ 后面是 ‘ A ‘),每一步走过的路径前必后开得染上当前的颜色。问最后的颜色状态。

Rv017.png

$R,C\leq2000,N\leq10^{18}$

看到 $N$ 的数据范围就知道本题的复杂度肯定与输出同阶了。

发现当 $N$ 很大时,每走一步一定是可以覆盖一行或者一列的,那么影响最后的颜色状态的步数大概与 $R+C$ 同阶 。

于是我们就暴力模拟最后的步数,直到所有位置都染上颜色为止。

至于如何模拟,最重要的就是求出每一步结束的位置,推一波公式:
$$
\left{
\begin{aligned}
r_i&=I+(-1)^{\left\lfloor \frac{i-1}{2}\right\rfloor}\left(\left\lfloor \frac{i-1}{2}\right\rfloor+1\right)\
c_i&=J+(-1)^{\left\lfloor \frac{i+2}{2}\right\rfloor}2\left\lfloor \frac{i+2}{4}\right\rfloor
\end{aligned}
\right.
$$
之后就随手模拟喽。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long ll;

const int MAXN=2005;

int R,C,I,J;ll N;

int mp[MAXN][MAXN];

inline void cPos(ll n,int &r,int &c)
{
ll tr=(ll)I+( (((n-1LL>>1LL)+1LL)&1LL) ? -1LL:1LL)*((n-1LL>>1LL)+1LL);
tr=(tr%(ll)R+(ll)R)%(ll)R;
ll tc=(ll)J+( ((n+2LL>>1LL)&1LL) ?-1LL:1LL)*((n+2LL>>2LL)<<1LL);
tc=(tc%(ll)C+(ll)C)%(ll)C;
r=tr,c=tc;
}

int main()
{
int i,r,c;
memset(mp,-1,sizeof mp);
scanf("%d%d%d%d%lld",&R,&C,&I,&J,&N);I--,J--;
int blk=R*C;
ll lv=N;
int rf,cf;cPos(N,rf,cf);
while(blk&&lv)
{
int clr=(lv-1)%26;
int r1,c1;cPos(lv,r1,c1);
if(lv&1)
{
if(lv>=R) {for(r=0;r<R;r++) if(mp[r][c1]==-1) mp[r][c1]=clr,blk--;}
else for(i=1;i<=lv;i++)
{
if(lv&2) r=(r1+R-i)%R;else r=(r1+i)%R;
if(mp[r][c1]==-1) mp[r][c1]=clr,blk--;
}
}
else
{
if(lv>=C) {for(c=0;c<C;c++) if(mp[r1][c]==-1) mp[r1][c]=clr,blk--;}
else for(i=1;i<=lv;i++)
{
if(lv&2) c=(c1+C-i)%C;else c=(c1+i)%C;
if(mp[r1][c]==-1) mp[r1][c]=clr,blk--;
}
}
lv--;
}
mp[rf][cf]=-1;
for(r=0;r<R;r++)
{
for(c=0;c<C;c++)
{
putchar(r==rf&&c==cf?'@':((~mp[r][c])?mp[r][c]+'A':'.'));
}
putchar('\n');
}
return 0;
}

B 线段树的匹配

一棵树的匹配是指一个树边集合,使得任意两条边没有公共端点。给定一个区间长度 $n$ ,求维护这个区间的线段树的最大匹配种类数。答案对 $998244353$ 取模。

$n\leq10^{18}$

注意到线段树是一个递归结构,对于固定的区间长度来说线段树的形态是固定的,那么最大匹配的种类数就是固定的了。维护长度为 $n$ 的区间的线段树最多有 $\log_2n$ 种形态,记忆化搜索即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
typedef long long ll;
const int MOD=998244353;

struct S{ll x,y;S(ll x=0,ll y=0):x(x),y(y){}};
S operator +(S a,S b)
{
if(a.x>b.x) return a;
if(a.x<b.x) return b;
return S(a.x,(a.y+b.y)%MOD);
}
S operator *(S a,S b){return S(a.x+b.x,a.y*b.y%MOD);}
struct P{S s0,s1;P(){}P(S s0,S s1):s0(s0),s1(s1){}};

std::map<ll,P> f;

P dp(ll l,ll r)
{
ll len=r-l+1;
if(f.count(len)) return f[len];
if(l==r) return f[len]=P(S(0,1),S(-1,0));
ll mid=(l+r)>>1;
P lc=dp(l,mid),rc=dp(mid+1,r);
S s0=(lc.s0+lc.s1)*(rc.s0+rc.s1),
s1=(lc.s0*(rc.s0+rc.s1))+(rc.s0*(lc.s0+lc.s1));
if(s1.x>=0) s1.x++;
return f[len]=P(s0,s1);
}

int main()
{
ll n;scanf("%lld",&n);
P ans=dp(1,n);ans.s0=ans.s0+ans.s1;
printf("%lld %lld",ans.s0.x,ans.s0.y);
return 0;
}