Codeforces Round #680 (Div. 2 可撤销并查集)

这场比赛

A - Array Rearrangment

思路:正排序a,逆排序b,找ai+bi是否大于某个值

B - Elimination

题面

a表示第一场前100最低分,c表示第二场前100最低分

b表示第一场前100的某个人在第二场的最低分

d表示第二场前100的某个人在第一场的最低分

求两场成绩第100名最小值分数

思路:max(a+b,c+d)

C - Division

题面:输入q(小于1e18),p(小于1e9),求q的最大因子 % p !=0

思路

1.q % p !=0直接输出 q

2.因为q太大了,求质因子要特殊方法(比如Pollard_rho),所以我们去判断 p 的质因子,如果q没有这个属于p的质因子,那么他就是备胎之一,如果有把q除到没有这个质因子,也变成了备胎之一。
最后把最大的备胎找到就行了

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define lson(x) x<<1
#define rson(x) x<<1|1
using namespace std;
const int N=1e5+10;
ll p;
int q;
int a[100],num[100],cnt;
void prime(int x){
    int k=sqrt(x);
    for(int i=2;i<=k;i++){
        if(x%i==0){
            a[cnt]=i;
            int nu=0;
            while(x%i==0){
                nu++;x/=i;
            }
            num[cnt++]=nu;
        }
    }
    if(x!=1){
        num[cnt]=1;a[cnt++]=x;
    }
    return ;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%d",&p,&q);
        cnt=0;
        if(p%q!=0){
            printf("%lld\n",p);continue;
        }
        prime(q);
        ll ans=1;
        for(int i=0;i<cnt;i++){
            ll zhi=1;//cout<<a[i]<<num[i]<<endl;
            for(int j=1;p%zhi==0;j++){
                zhi*=(ll)a[i];
                if(p%zhi==0 && (p/zhi)%(ll)q!=0){
                    ans=max(ans,p/zhi);
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/*
*/

D - Divide and Sum

题面吐槽,这个题面,一开始以为是子串,后面看了样例解释是随便选n个

给2*n的序列,你要把他分成两份n序列(x[],y[]),第一份从小到大排列,第二份从大到小排列,求每种分法的|xi-yi|之和,取模

思路

分法很好想,排列组合C(n,2 * n)即可,但是|xi-yi|一开始没有任何思路,然后我枚举了一下1 2 3 4

选1 2 | 3 4 最后得4

选1 3 | 2 4 最后得4

选1 4 | 2 3 最后得4

啊这,然后就是一个前缀和的问题了,从小到大排序前n个减,后n个加 * C(n,2 * n)。【上述结论,数学菜鸡无法解答。。。然后排列组合那里需要逆元】

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define lson(x) x<<1
#define rson(x) x<<1|1
#define mod 998244353
using namespace std;
const int N=3e5+10;
int n;
int a[N];
ll ksm(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans*=a;ans%=mod;
        }
        a*=a;a%=mod;
        b>>=1;
    }
    return ans;
}
ll C(ll a,ll b){
    ll ans=1;
    for(ll i=a;i>b;i--){
        ans*=i;ans%=mod;
    }
    for(ll i=b;i>=1;i--){
        ans*=ksm(i,mod-2);ans%=mod;
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<2*n;i++){
        scanf("%d",&a[i]);
    }
    sort(a,a+2*n);
    ll sum1=0,sum2=0;
    for(int i=0;i<n;i++){
        sum1+=(ll)a[i];
    }
    for(int i=n;i<2*n;i++){
        sum2+=(ll)a[i];
    }
    ll ans=(sum2-sum1)%mod;
    printf("%lld\n",ans*C((ll)2*n,(ll)n)%mod);
    return 0;
}
/*
*/

E - Team-Building

好题!用一下午时间让我知道了,可撤回并查集别顺手写路径压缩,wa了n次

无向图并查集判奇数环思路

判断F(u)等不等于F(v),等于就是有奇数环

不等于就这样并

mere(u,v+n)

mere(u+n,v)

可撤回并查集(请勿路径压缩)模板

int F(int x){return f[x]==x?x:F(f[x]);}//如果这题wa29,可以注意一下不要路径压缩
void mere(int x,int y){
    int xx=F(x),yy=F(y);
    if(xx!=yy){
        if(sz[xx]>sz[yy])swap(xx,yy);
        sz[yy]+=sz[xx];
        f[xx]=f[yy];
        ne[++tot]=xx;
    }
}
void roll_back(int l){
    while(tot>l){
        int now=ne[tot--];
        sz[f[now]]-=sz[now];
        f[now]=now;
    }
}

题面:有n个点,m条边,k种颜色,n个点有不同颜色,求有多少两种颜色构成的图无奇数环,数据5e5

思路:有k种颜色,所以有k * (k-1)/2的可能,正着加法做肯定tle,所以我们去做减法

如果并查集同种颜色的边并起来,有奇数环k-1,但要将单色图并好,不同颜色的边存起来,排序。找不同颜色边(出现相同两种颜色)的个数,然后判断是否有奇数环,如果有答案-1,然后撤回并查集。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e6+10;
int f[N],a[N],ne[N],sz[N],n,tot,m,vis[N];
ll k;
struct node{
    int nu,nv,u,v;
    friend bool operator<(const node a,const node b){
        if(a.nv==b.nv){
            return a.nu<b.nu;
        }
        return a.nv<b.nv;
    }
}p[N];
void inint(){
    for(int i=1;i<=2*n;i++){f[i]=i;sz[i]=1;}
    tot=0;
}
int F(int x){return f[x]==x?x:F(f[x]);}//如果这题wa29,可以注意一下不要路径压缩
void mere(int x,int y){
    int xx=F(x),yy=F(y);
    if(xx!=yy){
        if(sz[xx]>sz[yy])swap(xx,yy);
        sz[yy]+=sz[xx];
        f[xx]=f[yy];
        ne[++tot]=xx;
    }
}
void roll_back(int l){
    while(tot>l){
        int now=ne[tot--];
        sz[f[now]]-=sz[now];
        f[now]=now;
    }
}
int main(){

    scanf("%d%d%lld",&n,&m,&k);
    inint();
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    ll tmp=k;
    int cnt=0;
    for(int i=0;i<m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        if(a[u]==a[v]){
            if(!vis[a[u]] &&F(u)==F(v)){
                tmp--;vis[a[u]]=1;//相同颜色的集合里有奇数环,直接颜色种类-1
                continue;
            }
            mere(u,v+n);
            mere(u+n,v);
        }
        else{
            if(a[u]>a[v])swap(u,v);//把颜色从小到大存入,方便排序找相同的两个颜色的图
            p[++cnt].u=u,p[cnt].v=v;p[cnt].nu=a[u];p[cnt].nv=a[v];
        }
    }
    ll ans=(tmp-1)*tmp/2;//cout<<ans<<endl;
    sort(p+1,p+cnt+1);
    int i,j;
    int Len=tot;//现在所有的颜色相同的点并在同一个集合里,每次两个不同颜色点相连之后撤回到Len的地方
    for(i=1;i<=cnt;i=j+1){
        roll_back(Len);//撤回到原来颜色相同的点并在同一个集合的位置
        for(j=i;j<cnt;){
            if(p[j].nu!=p[j+1].nu || p[j].nv!=p[j+1].nv){break;}
            j++;
        }
        if(vis[p[j].nu] || vis[p[j].nv]){continue;}
        for(int kk=i;kk<=j;kk++){
            if(F(p[kk].u)==F(p[kk].v)){ans--;break;}
            mere(p[kk].u,p[kk].v+n);
            mere(p[kk].u+n,p[kk].v);
        }
    }
    printf("%lld\n",ans);
	return 0;
}

posted @ 2020-11-06 16:37  ouluy  阅读(104)  评论(0编辑  收藏  举报