2019ccpc江西省赛

A Cotree

题目链接
题意:给出n个点,仅有n-2条边,会形成两棵树,让你连一条边,求最小的。dis(i,j)代表对于i和j两个点路径上的边数。
思路:刚开始认为两颗树无论怎么连都不会影响答案。k队写了个树形DP+并查集,随便连了两天WA了。问了zl,发现肯定会影响。。zl得出连两颗树最为密集处,然后我尝试连了两棵树的重心,过了。

#include<bits/stdc++.h>

#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
long long ans = 0;

int son[maxn];
int n;
int f[maxn];

struct edge{
  int to,next;
};

edge G[maxn<<1];
int head[maxn];
int edgeNum;

int mx,rt;
int Size;
int sz[maxn];

void add_edge(int a,int b)
{
    G[edgeNum].to=b;
    G[edgeNum].next=head[a];
    head[a]=edgeNum++;
}

void dfsroot(int u,int fa){

    sz[u]=1;
    int num=0;

    for(int i=head[u];i!=-1;i=G[i].next){
        int v=G[i].to;
        if(v==fa)
            continue;

        dfsroot(v,u);
        sz[u]+=sz[v];
        num=max(num,sz[v]);
    }

    num=max(num,Size-sz[u]);

    if(num<mx){
        mx=num;
        rt=u;
    }
}

void init() {
    memset(head,-1,sizeof head);
    for (int i = 1; i <= n; i++)f[i] = i;
}

int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}

void mix(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    f[fx] = fy;
}

void dfs(int x, int fa) {
    //cout << x << endl;
    son[x] = 1;
    for(int i=head[x];i!=-1;i=G[i].next){
        int y=G[i].to;
        if (y == fa)continue;
        dfs(y, x);
        son[x] += son[y];
        ans += 1ll * son[y] * (n - son[y]);
    }
}

int main() {
    scanf("%d", &n);    
    init();
    for (int i = 1; i < n - 1; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        add_edge(x,y);
        add_edge(y,x);
        mix(x, y);
    }
    int rt1 = find(1),rt2;
    int tot=0;
    for (int i = 1; i <= n; i++) {
        int fi=find(i);
        if (fi != rt1) {
            tot++; 
            rt2=fi;
        }
    }

    Size=n-tot;
    mx=inf;
    dfsroot(rt1,-1);

    rt1=rt;

    Size=tot;
    mx=inf;
    dfsroot(rt2,-1);

    rt2=rt;
    add_edge(rt1,rt2);
    add_edge(rt2,rt1);
    dfs(1, -1);
    printf("%lld\n",ans);
}

C Trap

题目链接
题意:给定n条可能存在重复的边,在边中任取四条,要求构成等腰四边形,且四边形满足互质。
思路:预处理互质数的表,预处理出现次数,n^2枚举上下底边情况,再二分查找满足条件的斜边个数。等腰梯形满足条件,三遍大于第四条边。

#include <bits/stdc++.h>

#define maxn 10005
typedef long long ll;
using namespace std;

int n;
int num[maxn];
vector <int>a,b,v[maxn];

void input(){
    int i,x;
    for(i=0;i<n;i++)
    {
        scanf("%d",&x);
        a.push_back(x);
        num[x]++; 
    }
}

void solve(){
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());

    for(int i=1;i<=10000;i++){
        if(num[i]>=2){
            b.push_back(i);
        }
    }

    for(int i=1;i<=10000;i++){
        for(int j=0;j<b.size();j++){
            if(__gcd(i,b[j])==1){
                v[i].push_back(b[j]);
            }
        }
    }

    ll ans=0;
    for(int i=0;i<a.size();i++){
        for(int j=i+1;j<a.size();j++){
            int k=(a[j]-a[i])/2+1;
            int GCD=__gcd(a[i],a[j]);
            int pos=lower_bound(v[GCD].begin(),v[GCD].end(),k)-v[GCD].begin();
            ans=ans+v[GCD].size()-pos;
            if(GCD==1){
                if(num[a[i]]==2&&a[i]>=k)ans--;
                if(num[a[j]]==2&&a[j]>=k)ans--;
            }
        }
    }
    printf("%lld\n",ans);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        a.clear();
        b.clear();
        for(int i=1;i<=10000;i++){
            num[i]=0;
            v[i].clear();
        }

        input();
        solve();    
    }
}

D Wave

题目链接
题意:对于给定数列,求一个数列满足,数列中至少两个元素,奇数位置都相同,偶数位置都相同,且奇数位置偶数位置值不同。求满足条件的最长子序列。
思路:想复杂了,k队的做法优化一下即可。枚举一个值作为奇数位置的值,将其之前的值省略,在奇数位置两两之间的的数都记录出现的次数。求最大出现次数即可。特判最后子序列长度为奇的情况。

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

int n, c;
int a[maxn];
int num[105];
int vis[105];

int v[maxn];
int vcnt = 0;

void input() {
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
}

void solve() {

    int ans = 0;
    int u=0;
    int ANS;
    for (int t = 1; t <= c; t++) {
        int flag = 0;
        int cnt = 0;
        ans = 0;
        memset(num, 0, sizeof num);
        for (int i = 1; i <= n; i++) {
            if (a[i] == t) {
                flag = 1;
                cnt++;

                for (int j = 0; j < vcnt; j++) {
                    vis[v[j]] = 0;
                }
                vcnt = 0;
                continue;
            }

            if (!flag)
                continue;

            if (!vis[a[i]]) {
                num[a[i]]++;
                vis[a[i]] = 1;
                v[vcnt++] = a[i];
            }

        }

        for (int i = 1; i <= c; i++) {
            if (num[i] > ans)
            {
                ans = num[i];
                u = i;
            }
        }

        flag = 1;
        //cout<<t <<' ' << ans << ' ' << u << endl;
        for (int i = n; i >= 1; i--) {
            if (a[i] == t)
                break;
            if (a[i] == u)
                flag = 0;
        }
        ANS = max(ANS,2 * ans + flag);
        //cout << ANS << endl;
    }
    printf("%d\n", ANS);
}

int main() {
    input();
    solve();
}

F String

题目链接
题意:给出长度为n的序列,每次在n个字母中选择1个字母,求能构成avin字符串的几率
思路:水

#include <bits/stdc++.h>

#define maxn 105
typedef long long ll;
using namespace std;

char a[maxn];
int gcd(int x, int y){
    int z = x % y;
    while(z){
        x = y;
        y = z;
        z = x % y;
    }
    return y;
}
int main(){
    int n;
    int num[4]={0};
    while(scanf("%d",&n)!=EOF){
        memset(num,0,sizeof num);
        scanf("%s",a);

        for(int i=0;i<strlen(a);i++){
            if(a[i]=='a')
                num[0]++;
            else if(a[i]=='v')
                num[1]++;
            else if(a[i]=='i')
                num[2]++;
            else if(a[i]=='n')
                num[3]++; 
        }

        ll fm=pow(n,4);
        ll fz=num[0]*num[1]*num[2]*num[3];
        if(fz==0)
        {
            printf("0/1\n");
            continue;
        }
        ll GCD=gcd(fm,fz);
        fm=fm/GCD;
        fz=fz/GCD;
        printf("%lld/%lld\n",fz,fm);
    }

    return 0;
}

G Traffic

题目链接

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#include<unordered_map>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
int east[1002], north[1002];
int main(void){
    int n, m, ans, i, flag;
    while(scanf("%d %d",&m,&n)!=EOF){
        ans = 0;
        for(i = 0;i < m;i++){
            scanf("%d",&east[i]);
        }
        for(i = 0;i < n;i++){
            scanf("%d",&north[i]);
        }
        while(1){
            flag = 1;
            unordered_map<int,int> mp;
            for(i = 0;i < m;i++){
                mp[east[i]]++;
            }
            for(i = 0;i < n;i++){
                mp[north[i] + ans]++;
            }
            unordered_map<int,int>::iterator iter;
            for(iter = mp.begin();iter != mp.end();iter++){
                if(iter->second > 1){
                    flag = 0;
                    break;
                }
            }
            if(flag){
                printf("%d\n",ans);
                break;
            }
            ans++;
        }
    }
    return 0;
}

H Rng

题目链接
题意:对于[1,n]的区间先选择一个区间[l1,r1],再选择一个区间[l2,r2]且满足r2<r1。求两个区间相交的概率
思路:概率打表找规律

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
const int mod = 1e9 + 7;
ll qpow(ll x,ll y){
    ll res = 1;
    while(y){
        if(y & 1){
            res = res * x % mod;
        }
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
int main(void){
    ll n;
    while(scanf("%lld",&n)!=EOF){
        printf("%lld\n",(n + 1) * n / 2 % mod * qpow(n * n % mod,mod - 2) % mod);
    }
    return 0;
}

I Budget

题目链接
题意:给出n个数,求n个数四舍五入之后的赚或者亏了多少
思路:水

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#include<unordered_map>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
char ch[52];
int main(void){
    int n, i, sum, len;
    double ans;
    while(scanf("%d",&n)!=EOF){
        sum = 0;
        for(i = 0;i < n;i++){
            scanf("%s",ch);
            len = strlen(ch);
            if(ch[len - 1] >= '5'){
                sum += 10 - ch[len - 1] + '0';
            }
            else{
                sum -= ch[len - 1] - '0';
            }
        }
        ans = sum / 1000.0;
        printf("%.3f\n",ans);
    }
    return 0;
}

J Worker

题目链接
题意:n个房间,有m个工作者,每个房间工作者工作ai时间,求一种分配方案使得每个房间工作者工作时间总和相同。
思路:求最小公倍数,题目中1018应该是1e18,需开long long

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
ll gcd(ll x, ll y){
    ll z = x % y;
    while(z){
        x = y;
        y = z;
        z = x % y;
    }
    return y;
}
ll lcm(ll x, ll y){
    return x / gcd(x,y) * y;
}
ll num[1202];
int main(void){
    ll n, m, i, temp, sum;
    while(scanf("%lld %lld",&n,&m)!=EOF){
        for(i = 0;i < n;i++){
            scanf("%lld",&num[i]);
        }
        if(n == 1){
            puts("Yes");
            printf("%lld\n",m);
        }
        else{
            temp = lcm(num[0],num[1]);
            for(i = 2;i < n;i++){
                temp = lcm(temp,num[i]);
            }
            sum = 0;
            for(i = 0;i < n;i++){
                sum += temp / num[i];
            }
            if(m % sum){
                puts("No");
            }
            else{
                puts("Yes");
                for(i = 0;i < n;i++){
                    if(i){
                        printf(" ");
                    }
                    printf("%lld",temp / num[i] * m / sum);
                }
                puts("");
            }
        }
    }
    return 0;
}

K Class

题目链接

#include<bits/stdc++.h>
using namespace std;
const int maxn = 7e3 + 5;


int main() {
    int x, y;
    scanf("%d%d", &x, &y);
    int a = (x + y) / 2;
    int b = a - y;
    cout << a * b << endl;
}