1. 程式人生 > 實用技巧 >CF1388A~D(DFS,拓撲排序)

CF1388A~D(DFS,拓撲排序)

A.Captain Flint and Crew Recruitment

題意:

定義了一種近似素數,當一個數可以用兩個素數的乘積表示時,稱他為近似素數。

現在請你把正整數n用四個不同的正整數的和表示,其中至少三個是近似素數。

題解:

直接分解成三個最小的近似素數和另外一個隨便什麼數的和即可。

#include<bits/stdc++.h>
using namespace std;
int t,n;
int main () {
    scanf("%d",&t);
    while (t--) {
        scanf("%d",&n);
        
if (n<=30) { printf("NO\n"); continue; } printf("YES\n"); //printf("6 10 14 %d\n",n-20); if (n-30==6||n-30==10||n-30==14) { printf("6 10 15 %d\n",n-31); } else { printf("6 10 14 %d\n",n-30); } } }
View Code

B.Captain Flint and a Long Voyage

題意:

給出一個長度n,請你找到最小的長度為n的十進位制數x,使得它轉換為2進位制後的的數去掉最後n位儘可能大。

題解:

測智題,對我這種智商低的選手不友好。答案就是前3/4是9,後1/4是8。

#include<bits/stdc++.h>
using namespace std;
const int maxn=11010;
int t,n;
int main () {
    scanf("%d",&t);
    while (t--) {
        scanf("%d",&n);
        
int tt=n/4; if (n%4) tt++; for (int i=1;i<=n-tt;i++) printf("9"); for (int i=n-tt+1;i<=n;i++) printf("8"); printf("\n"); } }
View Code

C.Uncle Bogdan and Country Happiness

題意:

給出一棵樹,每個節點代表一個城市,每個城市有人口。一開始所有人都在首都1。

給出一個序列h,表示猜測的每個城市的開心人數和不開心人數的差值。

現在所有人從首都1開始返回自己的家鄉,他們保持同步,到自己家鄉之後會停止,他們可以在任意時刻改變自己的心情,但變成不開心之後就永遠不開心了。

詢問是否可能h陣列的情況出現。

題解:

首先統計出每個節點的子樹人口數,以此排除無法構造差值的節點(比如奇偶性不同就無法構造)

然後通過給定的h[i]算出每個節點開心的人數,然後做第二遍DFS,看一下每個節點的開心的人數和它所有子節點的開心的人數之和,如果小於則不合法。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
vector<int> g[maxn];
typedef long long ll;
ll p[maxn];
ll h[maxn];
ll dfn[maxn];//記錄每個城市的子樹人口和 
ll wjm[maxn];//計算出每個節點必須開心的人數 
int t,n,m;
int f;
void dfs (int u,int pre) {
    dfn[u]=p[u];
    for (int v:g[u]) {
        if (v==pre) continue;
        dfs(v,u);
        dfn[u]+=dfn[v];
    }
}
void dfs1 (int u,int pre) {
    int sum=0;
    for (int v:g[u]) {
        if (v==pre) continue;
        dfs1(v,u);
        sum+=wjm[v];
    }
    //printf("%d %d %d\n",u,wjm[u],sum);
    if (sum>wjm[u]) f=0;
}
int main () {
    scanf("%d",&t);
    while (t--) {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) g[i].clear(),dfn[i]=0;
        for (int i=1;i<=n;i++) scanf("%lld",p+i);
        for (int i=1;i<=n;i++) scanf("%lld",h+i);
        for (int i=1;i<n;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(1,0);
        f=1;
        for (int i=1;i<=n;i++) {
        //    printf("%d %d\n",dfn[i],h[i]);
            //if (dfn[i]-2*abs(h[i])<0) f=0;
            if (dfn[i]<h[i]||dfn[i]<-h[i]) f=0;
            if ((dfn[i]%2)^(abs(h[i])%2)) f=0; 
            ll x=(dfn[i]+h[i])>>1;
            wjm[i]=x;
        }
        dfs1(1,0);
        if (f)
            printf("YES\n");
        else
            printf("NO\n");
    }
}
View Code

D.Captain Flint and Treasure

題意:

給出兩個序列a和b,一次操作你可以將ans+=a[i],a[b[i]]+=a[i]。詢問最後ans的最大值。

題解:

拓撲排序,遇到負權值的節點就跳過,減少它給答案帶來的貢獻。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
typedef long long ll;
ll a[maxn];
int b[maxn];
int inDegree[maxn];
int n;
vector<int> g[maxn];
ll ans;
void topo () {
    queue<int> q;
    for (int i=1;i<=n;i++) 
        if (inDegree[i]==0) {
            q.push(i);
        }
    
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        ans+=a[u];
        if (b[u]==-1) continue;
        if (a[u]>0) {
            g[u].push_back(b[u]);
            a[b[u]]+=a[u];
        }
        else {
            g[b[u]].push_back(u);
        }
        if (--inDegree[b[u]]==0) q.push(b[u]);
    }
    
}
int main () {
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",a+i);
    for (int i=1;i<=n;i++) {
        scanf("%d",b+i);
        if (b[i]>0) {
            inDegree[b[i]]++;
        }
    }
    topo();
    printf("%lld\n",ans);
    vector<int> wjm;
    for (int i=1;i<=n;i++)
        for (int v:g[i]) inDegree[v]++;
    for (int i=1;i<=n;i++) if (inDegree[i]==0) wjm.push_back(i);
    for (int i=0;i<n;i++) {
        printf("%d ",wjm[i]);
        for (int v:g[wjm[i]]) 
            if (--inDegree[v]==0) wjm.push_back(v);
    }
}
View Code