没想到一年前外培的题目放到了今天的考试题,这是为什么呢?非常的奇妙啊!

所以下面的内容是去年写的,非常的奇妙啊!

\(\mathcal{T}_1\) 对王之王

Description

小 W 希望写出一副对联,让它体现 \(n\) 行的相生相克。

小 W 的这副对联包含上联、下联和横批,各有 \(n\) 个字。每个字根据其偏旁的类型,可以记为一个 \(0\)\(n − 1\) 之间的整数。小 W 希望上联、下联和横批都是 \(0\)\(n − 1\)排列,我们将其分别记为 \(a, b, c\).

小 W 还希望上联、下联、横批都能体现 \(n\) 行的相生相克。具体来说,小 W 会有三种标准来衡量这副对联是否能体现 \(n\) 行的相生相克:

  1. 对于任意 \(0 \leqslant i < n\),有 \(a_i + b_i ≡ c_i \pmod n\)

  2. 对于任意 \(0 \leqslant i < n\),有 \(a_ib_i ≡ c_i \pmod n\)

  3. 对于任意 \(0 \leqslant i < n\),有 \(a^{b_i}_i ≡ c_i \pmod n\),注意在本题中 \(0^0\) 是无意义的。

现在,小 W 希望你在符合第 \(\rm T\) 种标准的条件下,构造出一副长为 \(n\) 的对联。

\(2\leqslant n\leqslant 2\cdot 10^5\).

我的想法

\(\text T=1\) 的情况就想到了 这道题,在考场上还 yy 了个感性理解。

\(\text T=3\) 打了个暴力走人。

Solution

\(\text T=2\)

结论 1:\(n\) 无平方因子。

证明:

设存在质数 \(p\) 使得 \(p^2\mid n\) 且此时有解。显然 $A_x ,B_x $ 中只要有一个是 \(p\) 的倍数,\(C_x \bmod n\) 就会是 \(p\) 的倍数,由于 \(A,B,C\)\(p\) 的倍数个数相同,因此必须满足当 \(p\mid A_x\) 时,有 \(p\mid B_x\).

考虑 \(C_x =p\),此时 $p\mid A_x ,p\mid B_x $,故 $p^2\mid C_x $,且 \(p^2\mid (C_x\bmod n)\),所以矛盾。

结论 2:只有 \(n=2\) 有解。

证明:

首先 \(n\) 必须无平方因子。任取质数 \(p\) 满足 \(p\mid n\)。设 \(m=n/p\),显然 \(\gcd(m,p)=1\).

考虑集合 \(S=\{ xm\mid x\in [1,p-1],x\in \mathbb{N}\}\),类似于证明 \(n\) 无平方因子的方式可以证明 \(A_x \in S\Longleftrightarrow B_x \in S\Longleftrightarrow C_x \in S\).

那么有

\[\begin{aligned} \prod_{x\in S}x&=\prod_{A_x \in S}A_x =\prod_{B_x \in S}B_x =\prod_{C_x \in S}C_x \\ &=\prod_{k=1}^{p-1}km\\ &=m^{p-1}(p-1)!\\ &\equiv 1\times (-1)\pmod p\\ &\equiv -1\pmod{ p}\end{aligned} \]

\(\displaystyle\left(\prod_{A_x \in S}A_x\right) \cdot \left(\prod_{B_x \in S}B_x\right) =\prod_{C_x \in S}C_x\),故 \((-1)\times(-1)\equiv -1\pmod p\),只有 \(p=2\) 满足条件,因此 \(n\) 的质因子只有 \(2\),只有 \(n=2\) 符合条件。

\(\text T=3\)

数学题滚出 OI!!!

\(\mathcal{T}_2\) 伊莉斯

Description

你处于一个 \(n×n\) 的网格空间中,一开始你位于 \((1, 1)\),而「门」位于 \((n, n)\)

你每一时刻可以选择从 \((i, j)\) 移动向 \((i, j + 1)\) 或者 \((i + 1, j)\)(我们记为向上和向右), 当然,你不能离开这个空间。

伊莉斯拥有 \(c^2\) 个魔眼,它们在网格上形成一个方阵,即位置 \((a + i, b + j)(0 \leqslant i < c, 0 \leqslant j < c)\) 上都有一个魔眼。初始它们都有一个阀值 \(t\),如果某一时刻你移动到了 \((x, y)\),那 么位于位置 \((i, j)\) 的魔眼阀值将会减少 \(|x − i| + |y − j|\),一旦有一个魔眼的阀值减到了 \(0\) 以下,你将会被伊莉斯石化(即使你已经到达了「门」)。但由于你的强对抗力,你可以走到存在魔眼的位置上。

现在,作为霸主权能的拥有者,你想要判断出是否存在一条路径可以安全地到达「门」。

如果不存在一条安全的路径,输出 Again,否则输出一行一个由 RU 两种字符构成的长 \(2(n−1)\) 的字符串,表示你每一个时刻移动的方向为向右和向上。如果存在多种方案,输出对应字符串字典序最小的方案。

\(n\leqslant 2\cdot 10^6,1\leqslant t\leqslant 10^{14}\).

我的想法

经过一只魔眼 \((x,y)\) 的最短路:从 \((1,1)\) 走到 \((x,y)\),再从 \((x,y)\) 走到 \((n,n)\).

然后还是不会,打了暴力走人。

Solution

\(\color{red}{\text{Disclaimer}}\):以下涉及大量感性证明。


先考虑比较简单的情况 —— 只有一只魔眼。由于题目只要求在路径合法的限制下最小化字典序,所以直接使用上文提出的最短路是不科学的。我们需要尽可能向右走。事实上,对于在 \((a,b)\) 右下方 的点 \((i,j)\),此时的最短路一定是从 \((i,j)\) 向上走走到 \((i,b)\),再走到 \((n,n)\)

具体可以这样证明(其实这也是上文提到的最短路构造方案的证明):假设从 \((i,j)\) 开始往右走了几步,最后从 \((i,p),p>b\) 进入 \((a,b)\) 的右上方。这个方案一定比直接走到 \((i,b)\),从 \((i,b)\) 向右走到 \((i,p)\) 更劣。你可以画几圈关于 \((a,b)\) 的等距线来观察。

从这里也可以发现,最劣的情况一定是先向右走到 \((n,j)\),再向上走到 \((n,n)\).

于是可以得出我们的策略:先向右走到 \((a,1)\),再判断此时向右走一步,之后根据最优方案能否安全到达,不停尽力向右走即可。

回到原问题。先考虑一下如何判断方案的存在性。

首先还是从 \((1,1)\) 随便走到 \((a,b)\) 这个位置,因为在这个区间魔眼与人的距离相当于魔眼与 \((a,b)\) 的距离加上人与 \((a,b)\) 的距离。这是固定的。

方案是否合法只取决于四个角上的魔眼。但是我并不会证明。所以对于内部,用 "右上上右右上上……右" 的走法。这是因为只要是从左下走到右上,右上左下两只魔眼的阈值减少量就是固定的,比如左下那只,初始减少 \(\delta=0\),之后的减少量每次都能增加 \(1\)。而这个方案也能保证左上右下两只魔眼的总阈值减少量相同(需要说明的是,它们的阈值减少量总和总是固定的)。

\((a,b)\) 走到 \((n,n)\) 仍然是随便走。

现在考虑如何使字典序最小。迁移一只魔眼的思路,先尽量往右走,再判断是否合法。首先可以将起点挪到 \((a,1)\)。对于在 "魔眼区左下角(指 \(x<a,y<b\) 的区域)"、"魔眼区"、"魔眼区右上角" 的右下角区域有一个共同的最优策略 —— 向上走。

现在只需要考虑魔眼区的最优策略。首先能想到整体路径是先走到魔眼区右上角,再随便走,于是上文的 "右上左下两只魔眼的阈值减少量是固定的" 和 "左上右下两只魔眼的总阈值减少量总和固定" 这两个结论仍然成立。

于是只用保证左上右下两只魔眼的阈值不小于零即可。考虑左上角魔眼的阈值减少量变化情况,事实上这也是上文单只魔眼的情况。同时,由于左上角魔眼阈值减少量越小等价于右下角魔眼阈值减少量越大,判断是否存在一种方案使得两只魔眼阈值不小于零也是比较容易的。

时间复杂度 \(\mathcal O(n)\).

Code

代码实现有一点恶心,但是调试还是比较顺利(?

有一个 \(\text{corner case}\) 没有考虑到。

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <vector>
# include <iostream>
using namespace std;
typedef long long _long;

const _long infty = 1e18;

vector <char> ans; 
int n,a,b,c,x,y,X[4],Y[4],L; 
_long t,d[4],ad[4],low,upp,jinx;

_long Abs(const _long& x) { return x>0?x:-x; }
int dis(int x,int y,int z,int u) {
    return Abs(x-z)+Abs(y-u);
}
void getMin(_long& tmp,_long& delta,const _long& A,const _long& B) {
    if(A<B) tmp=A; else tmp=B, delta-=L;
}
_long cost(int x,int y,int z,int u,int X,bool opt=0) {
    L = Abs(u-z); _long delta = 1ll*(1+L)*L/2, tmp;
    if(!opt) getMin(tmp,delta,dis(x,y,z,X),dis(x,y,u,X)); 
    else getMin(tmp,delta,dis(x,y,X,z),dis(x,y,X,u));
    return delta+tmp*L; // ignore the first dot
} // 0: across; 1: vertical
bool canReach(int x,int y,bool opt=0) {
    for(int i=0;i<4;++i) {
        ad[i] = cost(X[i],Y[i],x-1,x,y,0);
        if(opt) ad[i]=0;
        if(d[i]+ad[i]>t) return false;
    }
    if(x>a+c-1) { 
        for(int i=0;i<4;++i) {
            if(y<b) ad[i] += cost(X[i],Y[i],y,b,x,1);
            ad[i] += cost(X[i],Y[i],max(b,y),b+c-1,x,1);
            ad[i] += cost(X[i],Y[i],b+c-1,n,x,1);
            ad[i] += cost(X[i],Y[i],x,n,n,0);
            if(d[i]+ad[i]>t) return false;
        } 
    } else {
        for(int i=0;i<4;++i) {
            if(y<b) ad[i] += cost(X[i],Y[i],y,b,x,1);
            ad[i] += cost(X[i],Y[i],a+c-1,n,b+c-1,0);
            ad[i] += cost(X[i],Y[i],b+c-1,n,n,1);
            if(d[i]+ad[i]>t) return false;
        }
        ad[1] += cost(X[1],Y[1],max(b,y),b+c-1,x,1);
        ad[1] += cost(X[1],Y[1],x,a+c-1,b+c-1,0);
        ad[3] += cost(X[3],Y[3],max(b,y),b+c-1,x,1);
        ad[3] += cost(X[3],Y[3],x,a+c-1,b+c-1,0);
        if(d[1]+ad[1]>t || d[3]+ad[3]>t) return false;
        _long delta = cost(X[0],Y[0],max(b,y),b+c-1,x,1)+
                      cost(X[0],Y[0],x,a+c-1,b+c-1,0);
        _long sm = delta+
                   cost(X[2],Y[2],max(b,y),b+c-1,x,1)+
                   cost(X[2],Y[2],x,a+c-1,b+c-1,0);
        low = delta;
        upp = cost(X[0],Y[0],max(b,y),b+c-1,a+c-1,1)+
              cost(X[0],Y[0],x,a+c-1,max(b,y),0);
        jinx = min(upp, t-d[0]-ad[0]);
        if(Abs(jinx-upp)&1) -- jinx; 
        if(jinx<low) return false;
        _long oth = sm-jinx;
        if((Abs(oth-(sm-delta))&1) || oth+d[2]+ad[2]>t) return false;
    } return true;
}

void upd(int x,int y,bool opt) {
    if(!opt)   
        for(int i=0;i<4;++i)
            d[i] += cost(X[i],Y[i],x-1,x,y,0);
    else
        for(int i=0;i<4;++i)
            d[i] += cost(X[i],Y[i],y-1,y,x,1);
}

int main() {
    freopen("elis.in","r",stdin);
    freopen("elis.out","w",stdout);
    n=read(9), t=read(9ll), a=read(9), b=read(9), c=read(9);
    x=a, y=1; X[0]=X[1]=a, X[2]=X[3]=a+c-1;
    Y[0]=Y[3]=b+c-1, Y[1]=Y[2]=b;
    for(int i=1;i<a;++i) ans.emplace_back('R');
    for(int i=0;i<4;++i) d[i]=cost(X[i],Y[i],1,a,1,0);
    while(y<b+c-1 && x<n) {
        if(canReach(x+1,y)) ++x, ans.emplace_back('R'), upd(x,y,0);
        else ++y, ans.emplace_back('U'), upd(x,y,1);
    }
    if(!canReach(x,y,1)) return puts("Again"), (0-0);
    while(x<n) ++x, ans.emplace_back('R');
    while(y<n) ++y, ans.emplace_back('U');
    for(const auto& i:ans) putchar(i); puts("");
    return 0;
}
posted on 2022-07-10 10:27  Oxide  阅读(60)  评论(0编辑  收藏  举报