二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

Codeforces 2225

着飾られていたドレスは透明、
身穿的礼服不再显露光辉

綺羅びやかなジュエルも我楽多、
华丽的珠宝也黯然失色

Educational Codeforces Round 189 (Rated for Div. 2)

A. A Number Between Two Others

image

要求 \(z\)\(x\) 的倍数,且 \(y\) 不是 \(x\) 的倍数。直接用 \(y\) 除于 \(x\),可以保证中间每个整数都是 \(x\) 的倍数,如果 \(\frac{y}{x}=2\),就没有中间数所以是 NO,如果 \(\frac{y}{x}>2\),那么在 $[2,\frac{y}{x}-1]之间肯定有值不是 \(\frac{y}{x}\) 的因数。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,T,x,y;
inline void solve(){
    cin>>x>>y;
    int k=y/x;
    if(k==2){
        cout<<"NO\n";
    }
    else{
        cout<<"YES\n";
    }
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

B. Alternating String

image

只能执行一次,直接与规范子串 abababa 形式和 bababab 形式对比,如果错位段数超过 \(1\),那就是否。

代码实现是入队每个不匹配的位置,然后判断一下最后一个入队元素下标减去第一个入队元素下标再加一是否等于队列长度。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,T;
const int N=2e5+5;
char ch1[N],ch2[N];
string s;
inline bool check1(){
    vector<int> pos;
    for(int i=0;i<n;i++){
        if(ch1[i]!=s[i]){
            pos.push_back(i);
        }
    }
    if(pos.empty()) return true;
    if(pos.back()-pos[0]+1==pos.size()){
        return true;
    }
    else{
        return false;
    }
}
inline bool check2(){
    vector<int> pos;
    for(int i=0;i<n;i++){
        if(ch2[i]!=s[i]){
            pos.push_back(i);
        }
    }
    if(pos.empty()) return true;
    if(pos.back()-pos[0]+1==pos.size()){
        return true;
    }
    else{
        return false;
    }
}
inline void solve(){
    cin>>s;
    n=s.size();
    for(int i=0;i<n;i++){
        ch1[i]=(i&1)?'b':'a';
        ch2[i]=(i&1)?'a':'b';
    }
    if(check1()||check2()){
        cout<<"YES\n";
    }
    else{
        cout<<"NO\n";
    }

}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

C. Red-Black Pairs

image

因为只有两行,所以直接进行 \(dp\)

\[dp_i=min(dp[i-1]+\text{(当前列竖着两个值贡献)},dp[i-2]+\text{(横着两行匹配贡献)}) \]

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,T;
const int N=2e5+5;
string A,B;
int dp[N];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        dp[i]=LONG_LONG_MAX;
    }
    cin>>A>>B;
    A="."+A;
    B="."+B;
    dp[0]=0;
    dp[1]=(A[1]==B[1])?0:1;
    for(int i=2;i<=n;i++){
        dp[i]=min(dp[i-1]+((A[i]==B[i])?0:1),dp[i-2]+((A[i-1]==A[i])?0:1)+((B[i-1]==B[i])?0:1));
    }
    cout<<dp[n]<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

D. Exceptional Segments

image

直接打表,可以发现前缀和

\[\operatorname{pref}_i = \begin{cases} i, & \text{if } i \bmod 4 = 0 \\[4pt] 1, & \text{if } i \bmod 4 = 1 \\[4pt] i + 1, & \text{if } i \bmod 4 = 2 \\[4pt] 0, & \text{if } i \bmod 4 = 3 \end{cases} \]

所以答案区间只能选择 \(00\) 匹配或者 \(11\) 匹配。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,T,x;
const int mod=998244353;
inline int cnt0(int x){
    return 1+x/4+(x%4>=3?1:0);
}
inline int cnt1(int x){
    return x/4+(x%4>=1?1:0);
}
inline void solve(){
    cin>>n>>x;
    int c0=cnt0(x-1)%mod;
    int c1=cnt1(x-1)%mod;
    int a0=(cnt0(n)-cnt0(x-1))%mod;
    int a1=(cnt1(n)-cnt1(x-1))%mod;
    int ans=(c0*a0%mod+c1*a1%mod)%mod;
    cout<<ans<<'\n';
    return;
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

E. Covering Points with Circles

image

解决这个问题的关键思路是在平面上使用最优的圆包装。众所周知,这就是 六边形堆积

如果将半径为 \(r\) 的圆按六边形堆积排列,那么它们大约占据

\[\frac{\pi}{2 \sqrt 3} \approx 0.9069 \]

的面积。

这对解决问题有什么帮助?问题的关键在于,每个测试案例中都有相当多的点,而且这些点都是在一个大矩形内生成的。这意味着,如果我们把圆圈放在六边形网格上,那么它们很有可能会覆盖所有点中的 \(90.6\%\)

剩下的工作就是了解如何以整数坐标构建这样一个六边形网格,以及如何找到答案中出现的圆(因为网格上有无限多个圆)。

我们将如下构造网格:设

\[w = 2r \\ h = \lceil \sqrt 3 r \rceil \]

则网格上的任意一点的值为

\[\begin{cases} x = x_{0} + col \cdot w + (row \bmod 2) \cdot r \\ y = y_{0} + row \cdot h \end{cases} \]

作为起点( \(x_{0}, y_{0}\) ),我们可以随机取一个点。以这样一个网格的不同点为中心的任何两个圆都不会相交,因为它们的中心至少相距 \(2r\)

我们将按如下方法寻找答案中应包含的圆:对于来自 \(p\) 的每个点,我们找到网格中包含某个点的最近的几行,以及网格中包含某个点的最近的几列,然后检查这些行和列的交点。

在实际操作中,只需找出 \(5\) 最近的行和列即可。

在这种形式下,如果运气好的话,解可能已经 "接受 "了。但如果运气不好呢?

对于这样的问题,有一种有效的方法。如果我们找不到解,那么我们只需再试一次,即选择另一个起点( \(x_{0}, y_{0}\) ),并相对于它建立网格。

随机出 \(x_0\),\(y_0\),之后对于每个点找出 \(5\times 5\) 相近的圆心,在还原回去,如果包含就 cov++

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,T,r;
mt19937 rd(chrono::system_clock::now().time_since_epoch().count());
inline int R(int l,int r){
    return rd()%(r-l+1)+l;
}
inline bool incircle(int x,int y,int ox,int oy,int r){
    return (ox-x)*(ox-x)+(oy-y)*(oy-y)<=r*r;
}
inline void solve(){
    cin>>n>>r;
    vector<pair<int,int>> p(n+1);
    for(int i=1;i<=n;i++){
        cin>>p[i].first>>p[i].second;
    }
    int w=2*r,h=ceil(sqrt(3)*r);
    int cov;
    vector<pair<int,int> > ans;
    do{
        ans.clear();
        cov=0;

        int x0=R(0,100000),y0=R(0,100000);
        auto check=[&](int x,int y){
            int nearrow=(y-y0)/h;
            for(int row=nearrow-2;row<=nearrow+2;row++){
                int nearcol=(x-(x0+(row%2)*r))/w;
                for(int col=nearcol-2;col<=nearcol+2;col++){
                    int xl=x0+col*w+(row%2)*r;
                    int yl=y0+row*h;
                    if(incircle(x,y,xl,yl,r)){
                        ans.push_back({xl,yl});
                        cov++;
                        return;
                    }
                }
            }
        };
        for(int i=1;i<=n;i++){
            check(p[i].first,p[i].second);
        }
    }while(cov*100<89*n);
    sort(ans.begin(),ans.end());
    ans.erase(unique(ans.begin(),ans.end()),ans.end());
    cout<<ans.size()<<'\n';
    for(auto [x,y]:ans){
        cout<<x<<" "<<y<<'\n';
    }
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    // cin>>T;
    T=1;
    while(T--){
        solve();
    }
    return 0;
}

F. String Cutting

image

遍历每个数,求出左侧和右侧最少占用长度,剩下的就是待选区间,在使用双哈希来判断当前记录答案和最终答案字符串顺序先后。判断两个字符串可以先二分出最大公共前缀串 \(lcp\),在判断下一位哪个大即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
string S;
const int N=1e6+5;
const int mod=1e9+7;
const int base=13331;
const int mod2=1e9+9;
int h[N],Base[N];
int h2[N],Base2[N];
inline pair<int,int> GetHash(int l,int r){
    return {((h[r]-h[l-1]*Base[r-l+1]%mod)+mod)%mod,((h2[r]-h2[l-1]*Base2[r-l+1]%mod2)+mod2)%mod2};
}
int n,T,l,k;
inline bool cmp(int L1,int R1,int L2,int R2){
    int len1=R1-L1+1;
    int len2=R2-L2+1;
    int minlen=min(len1,len2);

    int LL=1,RR=minlen,lcp=0;
    while(LL<=RR){
        int mid=(LL+RR)>>1;
        if(GetHash(L1,L1+mid-1)==GetHash(L2,L2+mid-1)){
            lcp=mid;
            LL=mid+1;
        }
        else{
            RR=mid-1;
        }
    }
    if(lcp==minlen){
        return len1>len2;
    }
    else{
        return S[L1+lcp]>S[L2+lcp];
    }
}
inline void solve(){
    cin>>n>>l>>k;
    cin>>S;
    if(n<l*k){
        cout<<"NO\n";
        return;
    }
    cout<<"YES\n";
    S="."+S;
    if(k==1){
        cout<<S.substr(1)<<'\n';
        return;
    }
    h[0]=0;
    h2[0]=0;
    for(int i=1;i<=n;i++){
        h[i]=(h[i-1]*base+(S[i]))%mod;
        h2[i]=(h2[i-1]*base+(S[i]))%mod2;
    }
    int bestL=-1,bestR=-1;
    
    for(int i=1;i<=n;i++){
        if(i>1&&i-1<l) continue;
        int maxpre=(i-1)/l;
        int youneed=max(0LL,k-1-maxpre);
        int youminlen=youneed*l;
        int R=n-youminlen;
        if(R-i+1<l) continue;

        if(bestL==-1){
            bestL=i,bestR=R;
        }
        else{
            if(cmp(i,R,bestL,bestR)){
                bestL=i,bestR=R;
            }
        }
    }
    for(int i=bestL;i<=bestR;i++){
        cout<<S[i];
    }
    cout<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    Base[0]=1;
    Base2[0]=1;
    for(int i=1;i<=1000000;i++){
        Base[i]=Base[i-1]*base%mod;
        Base2[i]=Base2[i-1]*base%mod2;
    }
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @ 2026-04-25 18:48  超绝最可爱天使酱  阅读(33)  评论(0)    收藏  举报