2019江西省程序设计竞赛

A.Cotree

题意:

两棵树,你需要连接两个点使得\(\sum_{i=1}^{i=n}{\sum_{j=i+1}^{j=n}}{dis(i,j)}\)最小。

思路:

对于每棵树进行换根dp,对每棵子树找到一个点使得所有点到这个点的距离和最小,连接这两个点,然后进行一次DFS统计每条边的贡献,累加即可。

code

#include<bits/stdc++.h>

using namespace std;
const int N = 1e5+100;
vector<long long> G[N];
int n,n1,n2;
bool vis[N];
long long ans;
void dfs1(int x){
    if(vis[x]) return ;
    vis[x]=1;
    for(auto v:G[x]){
        dfs1(v);
    }
}
int ta,tb,now;
long long dp[N];long long siz[N];
void dfs2(long long u,long long fa){
    siz[u]=1;
    for(auto v:G[u]){
        if(v==fa) continue;
        dfs2(v,u);
        siz[u]+=siz[v];
        dp[u]+=dp[v];
    }
    dp[u]+=siz[u];
}
void dfs3(long long u,long long fa){
    if(ans>dp[u]){
        ans=dp[u];
        now=u;
    }
    for(auto v:G[u]){
        if(v==fa) continue;
        long long res=dp[u]-dp[v]-siz[v];
        dp[v]=(dp[v]+res+n1-siz[v]);
        dfs3(v,u);
    }
}
void dfs4(long long u,long long fa){
    if(ans>dp[u]){
        ans=dp[u];
        now=u;
    }
    for(auto v:G[u]){
        if(v==fa) continue;
        long long res=dp[u]-dp[v]-siz[v];
        dp[v]=(dp[v]+res+n2-siz[v]);
        dfs4(v,u);
    }
}

long long final=0;
void dfs(int now,int fa){
    final+=siz[now]*(n-siz[now]);
    for(auto v:G[now]){
        if(v==fa) continue;
        dfs(v,now);
    }
}
int main(){
    //freopen("1.in","r",stdin);
    cin>>n;
    int u,v;
    for(int i=1;i<=n-2;i++){
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int a,b;//找到左子树的一个点和右子树的一个点
    a=1;dfs1(1);
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            b=i;n2++;
        }
    }
    n1=n-n2;
    ans=99999999999999;
    now=a;
    dfs2(a,0);
    dfs3(a,0);
    ta=now;
    memset(dp,0,sizeof(dp));
    memset(siz,0,sizeof(siz));
    ans=99999999999999;
    now=b;
    dfs2(b,0);
    dfs4(b,0);
    tb=now;
    G[ta].push_back(tb);
    G[tb].push_back(ta);
    memset(dp,0,sizeof(dp));
    memset(siz,0,sizeof(siz));
    dfs2(1,0);
    dfs(1,0);
    cout<<final<<endl;
    return 0;
}

D.Wave

题意:

给一个数组,找到最长的一个子序列使得这个子序列的所有奇数位的数字相同,所有偶数位的数字相同,奇数位和偶数位的数字不同。

思路:

利用一个前缀和可以快速地查询\([l,r]\)区间内\(c\)的数量,记录下每个数字出现的位置,暴力枚举子序列的那两个不同的数字,更新最大值。

code

#include<bits/stdc++.h>

using namespace std;
const int N = 1e6+100;
vector<int> pos[200];
int num[N][201];
int a[N];
int main(){
    int n,c;
    cin>>n>>c;
    for(int i=1;i<=100;i++){
        pos[i].push_back(0);
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        pos[a[i]].push_back(i);
        for(int j=1;j<=100;j++) num[i][j]=num[i-1][j];
        num[i][a[i]]++;
    }
    int ans=0;
    for(int i=1;i<=c;i++){
        for(int j=1;j<=c;j++){
            if(i!=j){
                int now=0;
                for(int k=2;k<(int)pos[i].size();k++){
                    if(num[pos[i][k]][j]-num[pos[i][k-1]][j]>0) now++;
                }
                now=now*2+1;
                if(num[n][j]-num[pos[i][(int)pos[i].size()-1]][j]>0){
                     now++;
                }
                ans=max(ans,now);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

F.String

题意:

给定一个字符串,随机拿出四个字母,求这四个字母组成"avin"的概率。

思路:

答案即为\(\frac{num['a']*num['v']*num['i']*num['n']}{|s|^4}\)

代码

#include<bits/stdc++.h>

using namespace std;
int main(){
    string s;int n;
    while(cin>>n>>s){
        long long a,b,c,d;
        a=b=c=d=0;
        for(int i=0;i<n;i++){
            if(s[i]=='a') a++;
            if(s[i]=='v') b++;
            if(s[i]=='i') c++;
            if(s[i]=='n') d++;
        }
        if(a*b*c*d==0){
            cout<<"0/1"<<endl;
        }
        else{
            long long x=a*b*c*d;
            long long y=(n*n*n*n);
            long long gg=__gcd(x,y);
            x/=gg;
            y/=gg;
            cout<<x<<'/'<<y<<endl;
        }
    }
    return 0;
}

G.Traffic

题意:

一个十字路口,东西和南北各有车辆经过,每个车到达十字路口的时间为\(t_i\),但是如果东西走向的汽车和南北走向的汽车同时到达十字路口,那么南北走向的就得等待。问最少要等多长时间。

思路:

暴力枚举等待时间,知道不会再有车发生时间冲撞。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 3000;
long long a[N],b[N];
bool ta[N],tb[N];
int main(){
    int n,m;
    while(cin>>n>>m){
        memset(ta,0,sizeof(ta));
        memset(ta,0,sizeof(tb));
        for(int i=1;i<=n;i++) cin>>a[i],ta[a[i]]=1;
        for(int i=1;i<=m;i++) cin>>b[i];
        int res=999999999;
        for(int i=0;i<=1001;i++){
            int ans=0;
            bool flag=0;
            memset(tb,0,sizeof(tb));
            for(int j=1;j<=m;j++){
                tb[b[j]+i]=1;
                if(ta[b[j]+i]==1){
                    flag=1;break;
                }
            }
            if(!flag){
                res=i;break;
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

H.Rng

题意:

一个\([1,n]\)的区间,执行下列操作:
1.任意在\([1,n]\)中选择一个\(r\)
2.在\([1,r]\)中选择一个\(l\)
计算两次线段相交的概率

思路:

手动计算2的情况为\(\frac{3}{4}\),暴力打表发现接近\(\frac{1}{2}\),猜测答案为\(\frac{n+1}{2*n}\)

代码:

#include<bits/stdc++.h>

using namespace std;
const int mod = 1e9+7;
long long qp(long long a,long long b){
    long long ans=1;
    while(b){
        if(b&1){
            ans=(ans%mod*a%mod)%mod;
        }
        a=(a%mod*a%mod)%mod;
        b>>=1;
    }
    return ans;
}
int main(){
    int n;
    while(cin>>n){
        cout<<((n+1)%mod*qp(2*n,mod-2)%mod)%mod<<endl;
    }
    return 0;
}

I.Budget

题意:

\(n\)个保留三位的小数,计算他们四舍五入变为2位后的差值

思路:

以string读入,判断最后一位即可。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 2000000;
string a[N];
int main(){
    int n;
    while(cin>>n){
        double ans=0;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            int l=(int)a[i].length();
            if(a[i][l-1]<='4'){
                ans-=((a[i][l-1]-'0')*1.0)/(1000);
            }
            else{
                ans+=((10-(a[i][l-1]-'0'))*1.0)/1000;
            }
        }
        printf("%.3lf\n",ans);
    }
    return 0;

J.Worker

题意:

\(n\)个工作间,\(m\)个人,第\(i\)个工作间每个人每天的产量位\(a_i\),计算一种分配方案使的每个工作间的产量相同,如果没有,输出"No"。

思路:

\(lcm\)分配即可。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 3000;
long long lcm(long long a,long long b){
   return a*b/__gcd(a,b);
}
int a[N],b[N];
int main(){
   long long n,m;
   while(cin>>n>>m){
       long long all=1;
       for(int i=1;i<=n;i++) cin>>a[i],all=lcm(all,a[i]);
       long long zz=0;
       for(int i=1;i<=n;i++){
           b[i]=all/(a[i]);
           zz+=b[i];
       }
       if(m%zz){
           cout<<"No"<<endl;
       }
       else{
           cout<<"Yes"<<endl;
           for(int i=1;i<=n-1;i++) cout<<m/(zz)*b[i]<<' ';
           cout<<(m/zz)*b[n]<<endl;
       }
   }
}

K.Class

题意:

\(x=a+b,y=a-b\)
计算\(a*b\)

思路:

答案为\(\frac{(x+y)*(x-y)}{4}\)

代码

#include<bits/stdc++.h>

using namespace std;
int main(){
   long long x,y;
   cin>>x>>y;
   cout<<(x+y)*(x-y)/4<<endl;
   return 0;
}
posted @ 2020-01-24 17:17  codancer  阅读(327)  评论(0编辑  收藏  举报