1. 程式人生 > >Educational Codeforces Round 36 (Rated for Div. 2) 題解

Educational Codeforces Round 36 (Rated for Div. 2) 題解

區間更新 pan 操作數 num Education namespace inline 數據 int

Educational Codeforces Round 36 (Rated for Div. 2)

題目的質量很不錯(不看題解做不出來,笑

Codeforces 920C

題意

給定一個\(1\)\(n\)組成的數組,只可以交換某些相鄰的位置,問是否可以將數組調整為升序的

解題思路

首先如果每個數都能通過交換到它應該到的位置,那麽就可以調整為升序的。

但實際上交換是對稱的,如果應該在的位置在當前位置前方的數都交換完成,那麽整體就是排好序的,因為不可能所有不在相應位置的數都在相應位置的後方。

所以我們從\(1\)\(n\)掃一遍,在掃描的過程中維護當前位置能向前交換的位置的最小值,這樣就可以判斷了

AC代碼

#include <bits/stdc++.h>
using namespace std;
int n,pos[300020],num[300020];
string str;
bool judge()
{
    int maxpre=1;
    for (int i=1;i<=n;i++)
    {
        if(i>=2)
            if(str[i-2]==‘0‘)   maxpre=i;
        if(pos[i]>=i)   continue;
        if(maxpre>pos[i])   return false
; } return true; } int main(int argc, char const *argv[]) { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%d",&num[i]); pos[num[i]]=i; } cin>>str; if(judge()) puts("YES"); else puts("NO"); return 0; }

Codeforces 920E

題意

給定一個無向圖,求補圖的連通分量數和每個連通分量的大小

點數\(n \le 200000\) ,邊數\(m \le 200000\)

(原題?:bzoj1098)

解題思路

雖然直接對補圖用\(BFS\)求連通分量數復雜度是\(O(n)\)的,但光建圖復雜度就要\(O(n^2)\)了,所以直接建補圖肯定是不行的

這裏用到一個神奇的方法,在對原圖\(BFS\)最開始維護一個所有未分配在連通分量中的點的集合,當前點不能到達的點就是補圖能到達的點,將補圖能到達的點加入隊列繼續\(BFS\),對於已經判斷在某個連通分量的點,需要在集合中刪去。

這個集合可以直接使用std::set<int>,也可以使用鏈表,在這裏我使用鏈表

這樣只需要建原圖就可以對補圖進行\(BFS\)了,復雜度\(O(n+m)\)

AC代碼

#include <bits/stdc++.h>
using namespace std;
const int maxn=200010;
vector <int> G[maxn];
int scc[maxn],scccnt;
int pre[maxn],nxt[maxn],vis[maxn],deled[maxn];
int n,m;
void del(int u)
{
    nxt[pre[u]]=nxt[u];
    pre[nxt[u]]=pre[u];
}
void bfs(int s)
{
    queue<int> Q;
    Q.push(s);
    del(s);
    while(!Q.empty())
    {
        int u=Q.front();Q.pop();
        if(vis[u])  continue;
        vis[u]=true;
        scc[scccnt]++;
        for (int v:G[u])
            deled[v]=true;
        for (int i=nxt[0];i<=n;i=nxt[i])
            if(!deled[i])
            {
                del(i);Q.push(i);
            }
        for (int v:G[u])
            deled[v]=false;
    }
    scccnt++;
}
int main(int argc, char const *argv[])
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n+1;i++)    pre[i]=i-1;
    for (int i=0;i<=n;i++)  nxt[i]=i+1;
    for (int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }

    for (int i=nxt[0];i<=n;i=nxt[i])
        if(!vis[i])
            bfs(i);

    sort(scc,scc+scccnt);
    printf("%d\n",scccnt);
    for (int i=0;i<scccnt;i++)
        printf("%d ",scc[i]);
    puts("");
    return 0;
}

Codeforces 920F

題意

定義\(D(x)= Card\{k:k| x,k \in N^+\}\)

給定數組\(a\),做如下操作:

replace l r:將\(a_l\)\(a_r\)替換為\(D(a_i)\)

sum l r:輸出\(a_l\)\(a_r\)的和

點數\(n \le 300000\),操作數\(m \le 300000\)\(a_i \le 1000000\)

解題思路

\(D(x)\)可以直接用篩法求出,復雜度\(O(MAXN \log MAXN)\)

首先顯然要用線段樹進行維護,但是直接套線段樹對區間的每個點都單點更新顯然是過不了的

註意到對\(D(x)\)叠代收斂很快,根據官方題解,在數據範圍內叠代不超過\(6\)次就可以收斂到\(2\)\(1\)

我們可以用\(2\)棵線段樹,一棵維護和,一棵維護最大值,當區間更新時對整個區間進行遞歸地更新,如果當前子樹的最大值$ \le 2$, 那麽可以直接返回,這樣對於每個點最多不更新超過\(6\)

復雜度\(O(m \log n+MAN \log MAXN)\)

AC代碼

#include <bits/stdc++.h>
using namespace std;
const int maxnum=1e6+6;
const int maxn=3e5+7;
int D[maxnum],a[maxn];
int n,m;
long long sumv[(maxn<<2)+5];
int maxv[(maxn<<2)+5];
void build(int now,int l,int r)
{
    if(l==r)
    {
        maxv[now]=sumv[now]=a[l];
        return;
    }
    int mid=l+((r-l)>>1);
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    sumv[now]=sumv[now<<1]+sumv[now<<1|1];
    maxv[now]=max(maxv[now<<1],maxv[now<<1|1]);
}
void update(int now,int l,int r,int ul,int ur)
{
    if(maxv[now]<=2)    return; //關鍵
    if(l==r)
    {
        maxv[now]=sumv[now]=D[sumv[now]];
        return;
    }
    int mid=l+((r-l)>>1);
    if(ul<=mid) update(now<<1,l,mid,ul,ur);
    if(ur>mid) update(now<<1|1,mid+1,r,ul,ur);
    sumv[now]=sumv[now<<1]+sumv[now<<1|1];
    maxv[now]=max(maxv[now<<1],maxv[now<<1|1]);
}
long long query_sum(int now,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr)    return sumv[now];
    int mid=l+((r-l)>>1);
    long long ans=0;
    if(ql<=mid) ans+=query_sum(now<<1,l,mid,ql,qr);
    if(qr>mid)  ans+=query_sum(now<<1|1,mid+1,r,ql,qr);
    return ans;
}

void pre_solve()
{
    for (int i=1;i<=1e6;i++)
    {
        for (int j=i;j<=1e6;j+=i)
            D[j]++;
    }
}

int main(int argc, char const *argv[])
{
    pre_solve();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,1,n);
    while(m--)
    {
        int t,l,r;scanf("%d%d%d",&t,&l,&r);
        if(t==1)
            update(1,1,n,l,r);
        else
            printf("%I64d\n",query_sum(1,1,n,l,r));
    }
    return 0;
}

Educational Codeforces Round 36 (Rated for Div. 2) 題解