Codeforces Round #636 (Div. 3) 全題解

題目鏈接:
A.Candies
B.Balanced Array
C.Alternating Subsequence
D.Constant Palindrome Sum
E.Weights Distributing
F.Restore the Permutation by Sorted Segments

  • A.Candies
    思路:簡單枚舉即可
    AC代碼:
void solve() {
    cin>>n;
    for(ll i=3,k=4;;i+=k,k*=2) {
        if(n%i==0) {
            cout<<n/i<<'\n';
            return ;
        }
    }
}
  • B.Balanced Array
    思路:簡單模擬
    AC代碼:
void solve(ll sum=0) {
    cin>>n;
    if(((n+1)/2)&1) {
        cout<<"NO\n";
        return ;
    }
    cout<<"YES\n";
    rep(i,n/2) {
        cout<<i*2<<' ';
        sum+=i*2;
    }
    rep(i,(n+1)/2-1) {
        cout<<i*2-1<<' ';
        sum-=i*2-1;
    }
    cout<<sum<<'\n';
}
  • C.Alternating Subsequence
    思路:貪心,連續相同符號的值取最大。
    AC代碼:
void solve(ll sum=0) {
    cin>>n;
    ll x,y=0;
    rep(i,n) {
        cin>>x;
        if(x*y>0) y=max(x,y);
        else sum+=y,y=x;
    }
    cout<<sum+y<<'\n';
}
  • D.Constant Palindrome Sum
    思路:對於每一對值,存在一個範圍,最終的x如果在這個範圍裏,那麼對於這對值只需要修改一個數或不需要修改,反之兩個數都需要修改。
    這個範圍D爲:[min(x,y)+1,max(x,y)+k]
    我們需要得到對於每一個可能的x,其在多少對元素的範圍D內,所以可以想到區間修改,單點查詢的解法。
    因爲是離線的,所以用差分數組+前綴和即可實現,我這裏用了樹狀數組實現。
    AC代碼:
void solve(ll ans=0x3f3f3f3f) {
    cin>>n>>k;
    rep(i,2*k) sum[i]=c[i]=0;
    rep(i,n/2) cin>>a[n/2-i+1];
    rep(i,n/2) {
        cin>>x;
        int l=min(x,a[i])+1;
        int r=max(x,a[i])+k;
        sum[x+a[i]]++;
        for(int j=l;j<=2*k;j+=j&-j) c[j]++;
        for(int j=r+1;j<=2*k;j+=j&-j) c[j]--;
    }
    rep(i,2*k) {
        ll t=0;
        for(int j=i;j;j-=j&-j) t+=c[j];
        ans=min(ans,n-t-sum[i]);
    }
    cout<<ans<<'\n';
}
  • E.Weights Distributing
    思路:預處理出每個點到a,b,c三點的距離(邊數),可以想到一個好的方案一定是a到b和b到c間包含儘可能多的重複,在此基礎上其餘邊儘可能少。
    可以貪心的認爲所有的重複路徑一定與b點直接相連(i.e.最終方案一定是這樣的:a走過幾條邊到某點d,經過重複利用的邊集v進入b,再走過v到d,最後走到c)於是我們只需要枚舉每個d點計算出max{dst(a to d)+dst(d to b)*2+dst(d to c)}即可。
    AC代碼:
const int N=4e5+5;
ll n,m,k,x,y,U,V,W;
ll sum[N],a[N],dst[4][N];
vector<int> ve[N];
 
void bfs(int V,int t) {
    queue<int> que;
    que.push(V),dst[t][V]=0;
    while(!que.empty()) {
        int u=que.front();
        que.pop();
        for(int v:ve[u]) if(dst[t][v]==-1) {
             dst[t][v]=dst[t][u]+1;
             que.push(v);
        }
    }
}
 
void solve(ll ans=1e18) {
    cin>>n>>m>>U>>V>>W;
    rep(j,3) rep(i,n) dst[j][i]=-1;
    rep(i,n) ve[i].clear();
    rep(i,m) cin>>a[i];
    sort(a+1,a+m+1);
    rep(i,m) sum[i]=sum[i-1]+a[i];
    rep(i,m) {
        cin>>x>>y;
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    bfs(V,1),bfs(U,2),bfs(W,3);
    rep(i,n) {
        ll d1=dst[1][i],d2=dst[2][i],d3=dst[3][i];
        if(d1+d2+d3>m) continue;
        ans=min(ans,sum[d1+d2+d3]+sum[d1]);
    }
    cout<<ans<<'\n';
}
  • F.Restore the Permutation by Sorted Segments
    思路:枚舉待求排列的第一位數,即可推出後續所有元素。
    具體實現:將每對l~r間所有元素存到集合中。每次刪除每個集合中當前推出的元素,刪除後哪個集合只剩下一個元素,這個元素就一定是原數組的下一個元素,如果出現多個或沒有這樣的元素,則說明方案不成立。
    從思路上說其實比E更簡單一些,只是代碼寫的稍有些亂。
    AC代碼:
bool check(int u) {
    ans[1]=u;
    rep(i,n) vis[i]=0;
    for(int v:ve[u]) vis[v]=1;
    rep(i,n-1) {
        int cnt=0,t;
        for(int v:ve[u]) {
            b[v].erase(u);
            if(b[v].size()==1) cnt++,t=v;
        }
        if(cnt^1) return false;
        if(vis[t]&&num[t]!=i+1) return false;
        u=ans[i+1]=*b[t].begin();
    }
    rep(i,n) {
        if(i-1) cout<<' ';
        cout<<ans[i];
    }
    cout<<endl;
    return true;
}

void solve() {
    cin>>n;
    rep(i,n) ve[i].clear(),a[i].clear(),b[i].clear();
    rep(i,n-1) {
        cin>>num[i];
        rep(j,num[i]) {
            cin>>x;
            ve[x].push_back(i);
            a[i].insert(x);
        }
    }
    rep(i,n) {
        rep(j,n) b[j]=a[j];
        if(check(i)) return ;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章