Codeforces Round 981 (Div. 3)

zhouruoheng發表於2024-10-28

Codeforces Round 981 (Div. 3) 總結

A

手推一下,發現位置變化為 \(-1,2,-3,4, \dots\),所以只需要看 \(n\) 的奇偶性即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=1;
int n;
void solve()
{
    cin>>n;
    if(n&1) puts("Kosuke");
    else puts("Sakurako");

}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}


B

操作時可以儘可能選擇多的格子,也就是對角線儘量延伸出去,所以統計每條對角線的最小值,\((i,j)\) 所在對角線編號可以為 \(i-j\),因為有負的所以加上 \(N\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=505;
int n;
int a[N][N],b[N*2];
void solve()
{
    cin>>n;
    memset(b,0,sizeof b);
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++) 
        {
            cin>>a[i][j];
            b[i-j+N]=min(b[i-j+N],a[i][j]);
        }
    ll ans=0;
    for(int i=0;i<N*2;i++) ans-=b[i];
    cout<<ans<<'\n';
}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

C

賽時 \(DP\) 寫的(開的 vp),然後感覺比 \(E\) 還煩人 qwq。估計很多人就是這樣的。設 \(f_{i,0/1}\) 為考慮前 \(i\) 個數,不交換或交換 \(i\)\(n-i+1\) 的最小貢獻,這樣每個狀態就都只與上一個有關了。那麼每個位置就是有兩種選擇,同樣上一個狀態也會有兩種情況,狀態轉移方程就好求了。

\[f[i][0]=min(f[i-1][0]+(a[i]==a[i-1])+(a[n-i+1]==a[n-(i-1)+1]),f[i-1][1]+(a[i]==a[n-(i-1)+1])+(a[n-i+1]==a[i-1])) \]

\[f[i][1]=min(f[i-1][0]+(a[n-i+1]==a[i-1])+(a[i]==a[n-(i-1)+1]),f[i-1][1]+(a[n-i+1]==a[n-(i-1)+1])+(a[i]==a[i-1])) \]

然後還需要注意初始值是最中間的位置,因此可以從最中間開始往後轉移,奇偶的初始化情況有點不同。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
int a[N],f[N][2];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) f[i][0]=f[i][1]=0;
    if(n&1)
    {
        int id=n/2+1;
        if(a[id]==a[id+1]) f[id+1][0]++;
        if(a[id]==a[id-1]) f[id+1][0]++;
        f[id+1][1]=f[id+1][0];
        for(int i=id+2;i<=n;i++) 
        {
            f[i][0]=min(f[i-1][0]+(a[i]==a[i-1])+(a[n-i+1]==a[n-(i-1)+1]),f[i-1][1]+(a[i]==a[n-(i-1)+1])+(a[n-i+1]==a[i-1]));
            f[i][1]=min(f[i-1][0]+(a[n-i+1]==a[i-1])+(a[i]==a[n-(i-1)+1]),f[i-1][1]+(a[n-i+1]==a[n-(i-1)+1])+(a[i]==a[i-1]));
        }
    }
    else 
    {
        int id=n/2+1;
        if(a[id]==a[id-1]) f[id][0]=f[id][1]=1;
        for(int i=id+1;i<=n;i++)
        {
            f[i][0]=min(f[i-1][0]+(a[i]==a[i-1])+(a[n-i+1]==a[n-(i-1)+1]),f[i-1][1]+(a[i]==a[n-(i-1)+1])+(a[n-i+1]==a[i-1]));
            f[i][1]=min(f[i-1][0]+(a[n-i+1]==a[i-1])+(a[i]==a[n-(i-1)+1]),f[i-1][1]+(a[n-i+1]==a[n-(i-1)+1])+(a[i]==a[i-1]));
        }
    }
    cout<<min(f[n][0],f[n][1])<<'\n';
}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

D

首先呢,怎麼找區間是美麗的,可以用字首和,設字首和陣列為 \(b\),當 \(b_i=b_j\) 時,說明 \([i+1,j]\) 是美麗的。那麼現在已經知道了一堆美麗的區間。

抽象一下題意,其實是經典的貪心題,就是問你有一堆已知左右端點的線段,選出最多數量的線段滿足其中沒有線段重疊。貪心策略是:儘可能早的確定一個右邊界,這樣對後面的影響是最小的。所以將線段按右端點排序,先將第一條放進去,將邊界確定為右端點。然後每次看下一條線段的左端點,大於邊界就能取,更新邊界;否則就放棄,看下一條。

然後這道題裡,可以直接用 \(map\) 離散化 \(b\)。然後依次掃過去,每次出現相同的數時就是出現了一條新的線段。這樣也能保證右端點小的先被考慮。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll a[N],b[N];
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],b[i]=b[i-1]+a[i];
    map<ll,ll> H;
    ll ans=0,ed=-1;
    H[0]=0;
    for(int i=1;i<=n;i++)
    {
        if(H.count(b[i]))
        {
            if(H[b[i]]>ed)
            {
                ans++;
                ed=i;
            }
        }
        H[b[i]]=i+1;
    }
    cout<<ans<<'\n';
}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

E

置換環問題,將 \(i\)\(p_i\) 連邊,那麼最後肯定會出現小於等於 \(n\) 個環,例如 2 3 1 5 6 7 4 8 9

img

這樣的話,就是要求每個環的個數小於等於 \(2\)。考慮最優交換策略。
如上圖四個節點的環這個環,交換環上任一節點的左右節點,比如交換 \(6\) 左右的兩個 \(p_5\)\(p_7\)\(5\) 是指向 \(6\) 的,交換就能使 \(7\) 指向 \(6\),而原本 \(6\) 是指向 \(7\) 的,\(5\) 指向 \(7\) 指向的 \(4\),就將 \(6\)\(7\) 獨立出去了,接下來繼續看環剩下的,直到大小不超過 \(2\)

也就是說每次操作都能保證消掉兩個,那麼對於一個環 \(x\) 來說,需要操作的次數即為 \((size_x-1)/2\)

用並查集統計環的大小。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=1e6+5;

int n;
int a[N];
int fa[N],siz[N];
bool v[N];
int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y) siz[x]+=siz[y],fa[y]=x;
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1,v[i]=0;
    for(int i=1;i<=n;i++) merge(i,a[i]);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int x=find(i);
        if(!v[x]) ans+=(siz[x]-1)/2;
        v[x]=1;
    }
    cout<<ans<<'\n';
}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

F

假設模數為 \(4\),模完以後得數列變為 \(1,1,2,3,1,0,1,1,2,3,1,0,1,\dots\),發現有迴圈節,試過幾個數後大膽假設所有模數都是這樣。

找到第一個為模後為 \(0\) 的位置,乘以 \(n\) 即可,嚴格證明我不會。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=1e6+5,mod=1e9+7;

ll n,k;
ll f[N];
void solve()
{
    cin>>n>>k;
    f[1]=f[2]=1;
    ll id,i=3;
    while(1)
    {
        f[i]=(f[i-1]+f[i-2])%k;
        if(!f[i]) 
        {
            id=i;
            break;
        }
        i++;
    }
    if(k==1) id=1;
    cout<<n%mod*(id%mod)%mod<<'\n';
}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

相關文章