牛客题单——同余、并查集

题单链接

Strange Way to Express Integers(表示整数的奇怪方式)

这道题之前已经写过了,不重复写了,下面是链接
中国剩余定理

程序自动分析

这道题很明显是用并查集解决的
如果两个数相等,就把两个数放入一个集合中
看完所有相等的数后在看不相等的数
如果存在两个不相等的数存在一个集合中,就不符合要求,反之就符合要求

但是这道题还有个坑是数据范围,要进行离散化!
我用的是一种用stl实现的非保序离散化

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int x[N],y[N],op[N];
int p[N];
int find(int x)
{
    
    
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    
    
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        int num=1;
        unordered_map<int,int>temp;
        for(int i=1;i<=n;i++)
        {
    
    
            int a,b;
            scanf("%d%d%d",&a,&b,&op[i]);
            if(!temp[a]) temp[a]=num++;
            x[i]=temp[a];
            if(!temp[b]) temp[b]=num++;
            y[i]=temp[b];
        }
        //for(int i=1;i<=n;i++) cout<<"*****"<<x[i]<<" "<<y[i]<<endl;
        for(int i=1;i<N;i++) p[i]=i;
        bool f=1;
        for(int i=1;i<=n;i++)
        {
    
    
            int px=find(x[i]),py=find(y[i]);
            if(op[i]==1) p[px]=py;
        }
        for(int i=1;i<=n;i++)
        {
    
    
            int px=find(x[i]),py=find(y[i]);
            if(op[i]!=1)
            {
    
    
                if(px==py)
                {
    
    
                    cout<<"NO"<<endl;
                    f=0;
                    break;
                }
            }
        }
        if(f==1) cout<<"YES"<<endl;
    }
    return 0;
}

食物链

这道题是一个并查集的应用,用并查集维护一个额外信息
在这里插入图片描述
这是一棵表示并查集的树,维护一个到根结点距离的额外信息

如果一个节点b有父节点a表示节点b可以吃父节点a,同样的对每个节点都有这个性质
那么就可以得到节点c必然可以被节点a吃,节点d必然和节点a是同类

这样我们就可以得到结论,设d=同一颗树上两节点到跟节点的距离之差
d%3=0,表示两节点同类
d%3=1,表示可以吃根
d%3=2,表示可以被根吃

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int n,k;
int p[N],d[N];
int find(int x)
{
    
    
    if(p[x]!=x)
    {
    
    
        int t=find(p[x]);
        d[x]+=d[p[x]];
        p[x]=t;
    }
    return p[x];
}
int main()
{
    
    
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) p[i]=i;
    int res=0;
    while(k--)
    {
    
    
        int t,x,y;
        scanf("%d%d%d",&t,&x,&y);
        if(x>n||y>n) res++;
        else if(t==1)
        {
    
    
            int px=find(x),py=find(y);
            if(px==py&&(d[x]-d[y])%3) res++; //在一个结合中且距离之差不是三的倍数说明不是同类
            else if(px!=py) //不在一个集合中才加到集合中去
            {
    
    
                p[px]=py;
                d[px]=d[y]-d[x];// (d[x]+?-d[y])%3==0
            }
        }
        else
        {
    
    
            int px=find(x),py=find(y);
            if(px==py&&((d[x]-d[y])%3+3)%3!=1) res++; //取模会出负数
            else if(px!=py)
            {
    
    
                p[px]=py;
                d[px]=d[y]+1-d[x]; //(d[x]+?-d[y]-1)%3==0
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

银河英雄传说

这道题和上一道题差不多,都是用并查集来维护一些额外的信息

在这道题中要维护一个到跟节点距离的数组和当前集合有多少战舰的数组

当第a列战舰接到第b列战舰的后面时,b列战舰的战舰数要更改整a列战舰战舰数+b列战舰战舰数,a列战舰的根到b列战舰的根的距离就是b列战舰的拥有战舰的数量

#include <bits/stdc++.h>
using namespace std;
const int N=3e4+10;
int n;
int p[N],d[N],s[N];
int find(int x)
{
    
    
    if(x!=p[x])
    {
    
    
        int t=find(p[x]);
        d[x]+=d[p[x]];
        p[x]=t;
    }
    return p[x];
}
int main()
{
    
    
    for(int i=1;i<N;i++) p[i]=i,s[i]=1;
    cin>>n;
    while(n--)
    {
    
    
        char op[2];
        int x,y;
        scanf("%s%d%d",op,&x,&y);
        if(op[0]=='M')
        {
    
    
            int px=find(x),py=find(y);
            p[px]=py;
            d[px]=s[py];
            s[py]+=s[px];
        }
        else
        {
    
    
            int px=find(x),py=find(y);
            if(px!=py) cout<<"-1"<<endl;
            else cout<<abs(d[x]-d[y])-1<<endl;
        }
    }
    return 0;
}

Parity Game(奇偶游戏)

这道题和前面两道题也非常相似,都是需要在维护并查集的同时维护一些额外的信息

首先用前缀和的思想来考虑这个问题[l,r]这个区间中有偶数个1,则说明[1,l-1]和[1,r]中均有偶数个或者奇数个1,也就是奇偶性相同。[l,r]这个区间中有奇数个1,则说明[1,l-1]和[1,r]奇偶性不同。因此只需要维护区间右端点的奇偶性即可

维护奇偶性的时候用位运算中的异或运算就会比较简单

#include <bits/stdc++.h>
using namespace std;
const int N=2e4+10;
int n,m;
int p[N],d[N];
unordered_map<int,int>temp;
int get_hash(int x)
{
    
    
    if(temp.count(x)==0) temp[x]=n++;
    return temp[x];
}
int find(int x)
{
    
    
    if(p[x]!=x)
    {
    
    
        int t=find(p[x]);
        d[x]^=d[p[x]];
        p[x]=t;
    }
    return p[x];
}
int main()
{
    
    
    cin>>n>>m;
    n=1;
    for(int i=1;i<N;i++) p[i]=i;
    for(int i=1;i<=m;i++)
    {
    
    
        int a,b;
        string op;
        cin>>a>>b>>op;
        a=get_hash(a-1),b=get_hash(b);
        int t=0;
        if(op=="odd") t=1;
        int pa=find(a),pb=find(b);
        if(pa==pb)
        {
    
    
            if(d[a]^d[b]!=t)
            {
    
    
                cout<<i-1<<endl;
                return 0;
            }
        }
        else
        {
    
    
            p[pa]=pb;
            d[pa]=d[a]^d[b]^t;
        }
    }
    cout<<m<<endl;
    return 0;
}

同余方程

这道题是扩展欧几里得算法求解线性同余方程的简单应用
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b, ll &x, ll &y)
{
    
    
    if (!b)
    {
    
    
        x = 1, y = 0;
        return a;
    }
    ll d = gcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
int main()
{
    
    
    ll x, y, a, b;
    cin >> a >> b;
    ll g = gcd(a, b, x, y);
    cout<<(x%b+b)%b<<endl;
    return 0;
}

Sumdiv

之前的题单里出现过这个题了,不重复写了
Sumdiv题单链接

The Luckiest Number(最幸运的数)

在这里插入图片描述
化简到这个形式,就可以想到欧拉定理了!

欧拉定理:如果a与n互质,则aφ(n)≡1 (mod n)

根据欧拉定理确实可以构造出一个解,但是怎么样求解一个最小的解呢?

给出结论最小的解必然是φ(n)的约数,然后证明:
在这里插入图片描述
这样这道题的做法就很明确了!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    
    
    return b?gcd(b,a%b):a;
}
ll get_phi(ll n)
{
    
    
    ll res=n;
    for(ll i=2;i<=n/i;i++)
    {
    
    
        if(n%i==0)
        {
    
    
            while(n%i==0) n=n/i;
            res=res*(i-1)/i;
        }
    }
    if(n>1) res=res*(n-1)/n;
    return res;
}
ll qmi(ll n,ll k,ll mod)
{
    
    
    ll res=1;
    while(k)
    {
    
    
        if(k%2==1) res=res%mod*n%mod;
        n=n%mod*n%mod;
        k/=2;
    }
    return res;
}
int main()
{
    
    
    ll l;
    for(int i=1;cin>>l&&l;i++)
    {
    
    
        ll g=gcd(8,l);
        ll c=9*l/g;
        ll res=1e17;
        if(c%2==0||c%5==0) res=0;
        else
        {
    
    
            ll phi=get_phi(c);
            for(ll i=1;i<=phi/i;i++)
            {
    
    
                if(phi%i!=0) continue;
                if(qmi(10,i,c)==1) res=min(res,i);
                if(qmi(10,phi/i,c)==1) res=min(res,phi/i);
            }
        }
        printf("Case %d: %lld\n",i,res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46126537/article/details/114021340