蓝桥备赛第四周 同余+并查集

文章目录

0x33 同余

同余类+剩余系+费马小定理+欧拉定理及推论

在这里插入图片描述
在这里插入图片描述


最幸运的数字

现在给定一个正整数L,请问至少多少个8连在一起组成的正整数
(即最小幸运数字)是L的倍数。
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含一个整数L。
当输入用例L=0时,表示输入终止,该用例无需处理。
输出格式
每组测试用例输出结果占一行。
结果为“Case 1:+一个整数N,N代表满足条件的最小幸运数字的位数。
如果满足条件的幸运数字不存在,则N=0。
数据范围
1≤L≤2109
输入样例:
8
11
16
0
输出样例:
Case 1: 1
Case 2: 2

题解

在这里插入图片描述

这次的代码很多东西:欧拉函数快速求解,gcd,快速乘,各种定理,建议当模板背

10LL 转换成长整型
快速乘+快速幂(取模)模板
技巧:试除法可以一半一半求,减少遍历次数,两半都用稠密的一半遍历(因子是成对的)
#include"stdio.h"
#include"string.h"
#include"math.h"
#include"algorithm"
using namespace std;
typedef long long ll;

ll L;

ll gcd(ll a,ll b)
{
    if(a < b)
       {
           ll t = a; a = b; b = t;
       }
    if(b == 0)
        return a;
    return gcd(b,a%b);
}
ll multi(ll a,ll b,ll mod)
{
    ll ans = 0;
    while(b)
    {
        if(b & 1)
            ans = (ans + a) % mod;
        a = (a << 1) % mod;
        b = b >> 1;
    }
    return ans;
}
ll Ola(ll n)
{
    ll sum = n;
    for(int i = 2; i * i<= n; i ++)
    {
        if(n % i)
            continue;
        sum = sum / i * (i - 1);
        while(n % i == 0)
            n = n / i;
    }
    if(n != 1)
        sum = sum / n * (n - 1);
    return sum;
}
int Quick(ll a,ll b,ll mod)
///a:10LL b:欧拉函数的因子,也就是次数 mod:9L/d
///这个就是快速幂,套了快乘的快速幂(因为mod可能很大)
{
    ll ans = 1; a %= mod;///a与n互质
    ///a %= mod;不加也能过
    while(b)
    {
        if(b & 1)
            ans = multi(ans,a,mod);
        b >>= 1;
        a = multi(a,a,mod);
    }
    if(ans == 1)
        return 1;
    return 0;
}
ll solve()
{
    ll g = L / gcd(L,8LL) * 9;
    ///LL转换,g是9L除以d
    if(gcd(10LL,g) != 1) return 0;
    ///a,n互质才有解
    ll sum = 1;
    ll num = Ola(g); ///把g的欧拉函数求出来
    ///下面这个for是试除法求因子
    for(int i = 1; i * (ll)i <= num; i ++)
    {
        if(num % i)  continue;
        ///是因子就看看能不能余g
        ///这个quick有个10LL的参数应该也是模板
        if(Quick(10LL,(ll)i,g) == 1)
            return i;
    }
    ///依旧是试除法求因子,不过求的是大于根号的这一半(因为小于根号这一半较小嘛,更好求)
    ll m = sqrt(num);
    for(int i = m; i >= 1; i --)
    {
        if(num % i == 0 && Quick(10LL,num / i,g) == 1)
            return num / i;
    }
    return 0;
}

int main()
{
    int cnt = 1;
    while(~scanf("%lld",&L))
    {
        if(L == 0)  break;
        printf("Case %d: %lld\n",cnt ++,solve());
    }
}


扩展欧几里得算法

ax+by=gcd(a,b)存在至少一组整数解xy

在这里插入图片描述
在这里插入图片描述

乘法逆元

在这里插入图片描述


sumdiv

新的逆元做法,下次再做吧


线性同余方程

在这里插入图片描述


同余方程

求关于x的同余方程 ax ≡ 1(mod b) 的最小正整数解。

输入格式
输入只有一行,包含两个正整数a,b,用一个空格隔开。

输出格式
输出只有一行,包含一个正整数x,表示最小正整数解。

输入数据保证一定有解。

数据范围
2≤a,b≤2109
输入样例:
3 10
输出样例:
7

就是exgcd的模板题

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll a,b,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{

    if(b==0){
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,x,y);
    int z=x;x=y;y=z-y*(a/b);
    return d;
}
int main()
{
    cin>>a>>b;
    exgcd(a,b,x,y);
    cout<<(x%b+b)%b<<endl;

    return 0;
}


中国剩余定理

在这里插入图片描述


表达整数的奇怪方式

给定 2n 个整数a1,a2,,an和m1,m2,,mn,求一个最小的非负整数 x,满足∀i∈[1,n],x≡mi(mod ai)。

输入格式
第1 行包含整数 n。

第 2..n+1行:每 i+1 行包含两个整数ai和mi,数之间用空格隔开。

输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在64位整数范围内。

数据范围
1≤ai≤2311,
0≤mi<ai
1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31

思路

在这里插入图片描述

在这里插入图片描述

看题解更好些

没法一口气求解,只能一个一个往上增加

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL exgcd(LL a, LL b, LL &x, LL &y){
    ///exgcd
    if(b == 0){
        x = 1, y = 0;
        return a;
    }

    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
LL inline mod(LL a, LL b){
    ///防止负数的取模(最小化到0~b-1)
    return ((a % b) + b) % b;
}
int main()
{
    cin>>n;
    LL a1,m1;
    cin>>a1>>m1;

    for(int i=1;i<n;i++)
    ///每输入一组都要合并
    {
        LL a2,m2,k1,k2;
        cin>>a2>>m2;
        LL d=exgcd(a1,-a2,k1,k2);
        ///
        if((m2-m1)%d){
        ///不是因数(不能整除),无解
            cout<<-1<<endl;
            return 0;
        }
        k1=mod(k1*(m2-m1)/d,abs(a2/d));
        m1=k1*a1+m1;///产生新的a1和m1
        a1=abs(a1/d*a2);
    }
    cout<<m1<<endl;

    return 0;
}

0x41并查集

这个东西很适合搞图的连通性==“擅长维护传递性”

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

模板

int fa[maxn];
void init()
{
    for(int i=0;i<maxn;i++)
    {
        fa[maxn]=i;
    }
}
int get(int x)
{
    if(x==fa[x])return x;
    return fa[x]=get(fa[x]);
}
int mergefa(int x,int y)
{
    fa[get(x)]=get(y);
}

程序自动分析

标准的并查集题

在这里插入图片描述
在这里插入图片描述

不想离散化,所以map版并查集,但会有一组TLE
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
map<int,int> fa;
int n;
int xi[maxn];
int yi[maxn];
int ei[maxn];
int get(int x)
{
    if(fa[x]==0||x==fa[x])
    ///这个if应该是短路运算符
    ///不过不加fa[x]==0||也行,已经把赋初值拆分了
    ///map没赋过值的为0
    {
        fa[x]=x;///给没赋过值的准备的
        return x;
    }
    return fa[x]=get(fa[x]);
}
void mergefa(int x,int y)
{
    fa[get(x)]=get(y);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        fa.clear();///初始化要做好
        int n;
        cin>>n;
        for(int i=0;i<n;i++)
        {
            cin>>xi[i]>>yi[i]>>ei[i];
        }
        ///先算一遍等于号的
        for(int i=0;i<n;i++)
        {
            int x=xi[i];
            int y=yi[i];
            if(ei[i]==1)
            {
                if(fa[x]==0)
                {
                    fa[x]=x;///赋初值拆分进来
                }
                if(fa[y]==0)
                {
                    fa[y]=y;///赋初值拆分进来
                }
                //
                mergefa(x,y);
            }
        }
        ///不等于的
        int flag=1;
        for(int i=0;i<n;i++)
        {
            int x=xi[i];
            int y=yi[i];
            if(ei[i]==0)
            {
                if(fa[x]==0)
                {
                    fa[x]=x;///赋初值拆分进来
                }
                if(fa[y]==0)
                {
                    fa[y]=y;///赋初值拆分进来
                }
                if(get(x)==get(y))
                {
                    flag=0;
                    break;
                }
                else{
                    continue;
                }
            }
        }
        if(flag)
        {
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }

    }



    return 0;
}

在map基础上,把相等的和不等的分开存储,空间*2,时间/2,牛客能过了,acwing多过了几条数据,但还是7组
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
map<int,int> fa;
int n;
int xi[maxn];
int yi[maxn];

int xp[maxn];
int yp[maxn];
int get(int x)
{
    if(x==fa[x])
    ///这个if应该是短路运算符
    ///不过不加fa[x]==0||也行,已经把赋初值拆分了
    ///map没赋过值的为0
    {
        fa[x]=x;///给没赋过值的准备的
        return x;
    }
    return fa[x]=get(fa[x]);
}
void mergefa(int x,int y)
{
    fa[get(x)]=get(y);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        fa.clear();///初始化要做好
        int n;
        cin>>n;
        int e;
        int bufx,bufy,bufe;
        int cnt1,cnt0;
        cnt1=cnt0=0;
        for(int i=0;i<n;i++)
        {
            cin>>bufx>>bufy>>bufe;
            if(bufe==1)
            {
                xi[cnt1]=bufx;
                yi[cnt1++]=bufy;
            }
            if(bufe==0)
            {
                xp[cnt0]=bufx;
                yp[cnt0++]=bufy;
            }
        }
         for(int i=0;i<cnt1;i++)
         {
            int x=xi[i];
            int y=yi[i];

            if(fa[x]==0)
            {
                fa[x]=x;///赋初值拆分进来
            }
            if(fa[y]==0)
            {
                fa[y]=y;///赋初值拆分进来
            }
            //
            mergefa(x,y);
         }
        ///不等于的
        int flag=1;
        for(int i=0;i<cnt0;i++)
        {
            int x=xp[i];
            int y=yp[i];

            if(fa[x]==0)
            {
                fa[x]=x;///赋初值拆分进来
            }
            if(fa[y]==0)
            {
                fa[y]=y;///赋初值拆分进来
            }
            if(get(x)==get(y))
            {
                flag=0;
                break;
            }
            else{
                continue;
            }

        }
        if(flag)
        {
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }

    }
    return 0;
}

我透了,改个unordered_map就能过,或者开O2(O2不能过)

在这里插入图片描述

2021.3.7复习解法(能过前九个):有个很不错的结构体写法

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
int fa[maxn];
struct uneq{
    int a;
    int b;
}uneqs[maxn];
void initfa()
{
    for(int i=0;i<maxn;i++)
    {
        fa[i]=i;
    }

}
int getfa(int x)
{
    if(fa[x]==x)return fa[x];
    return fa[x]= getfa(fa[x]);
}
int mergefa(int a,int b)
{
    fa[getfa(a)]=getfa(b);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        initfa();
        int n;
        cin>>n;
        int cntuneq=0;
        int bufa,bufb,bufc;
        while(n--)
        {
            cin>>bufa>>bufb>>bufc;
            if(bufc==1)
            {
                mergefa(bufa,bufb);
            }
            if(bufc==0)
            {
                uneqs[cntuneq++]={bufa,bufb};///新的结构体写法很有趣
            }
        }
//        for(int i=0;i<cntuneq;i++)
//        {
//            cout<< uneqs[i].a<<uneqs[i].b;
//        }
        int flag=1;
        for(int i=0;i<cntuneq;i++)
        {
            if(getfa(uneqs[i].a)==getfa(uneqs[i].b))
            {
                flag=0;
                break;
            }
        }
        if(flag)
        {
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }
    }
    return 0;
}


supermarket

0x17


扩展域与边带权的并查集

在这里插入图片描述
在这里插入图片描述


银河英雄传说

在这里插入图片描述
在这里插入图片描述

虽然dx的变化有一半是写在get里面的,但如果不merge,fa[x]指向的是当前根节点,根节点dx为0,dx+=d【fa【x】】不会发生改变-----------每一次merge,对每个节点只+1次,且不会影响其他同集合的节点

在这里插入图片描述

带图的题解

另外有的标准size是关键字,不能用,不过编译
#include <bits/stdc++.h>
using namespace std;
const int N=31000+10;
int fa[N],n,t,i,j,d[N],size[N];//size就是记录个数
int get(int x)
{
    if (x==fa[x])
        return x;
    int root=get(fa[x]);
    d[x]+=d[fa[x]];//往下推进
    return fa[x]=root;
}
void merge(int x,int y)
{
    x=get(x),y=get(y);
    fa[x]=y,d[x]=size[y];
    size[y]+=size[x];//顺带记录
}
int main()
{
    scanf("%d\n",&t);
    for(i=1;i<=30000;i++)
        fa[i]=i,size[i]=1;
    while(t--)
    {
        char ch=getchar();
        scanf("%d %d\n",&i,&j);
        if (ch=='M')
            {
                merge(i,j);
            }
        else
        {
            if (get(i)==get(j))
                cout<<abs(d[i]-d[j])-1;
            else
                cout<<"-1";
            cout<<endl;
        }
    }

    return 0;
}


银河英雄传说—边权集2021.3.8复习—有注释!

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100005
int fa[maxn];
int d[maxn];///记录战舰x与fa[x]之间边的权值///初值为0
int sizefa[maxn];/// 记录树的大小,初值1
void initfa()
{
    for(int i=0;i<maxn;i++)
    {
        fa[i]=i;
    }

}
int getfa(int x)
{
    if(fa[x]==x)return x;

    return fa[x]=getfa(fa[x]);
}
int newgetfa(int x)
{
    if(fa[x]==x)return x;

    int root=getfa(fa[x]);///先存出来,集合代表
    d[x]+=d[fa[x]]///树根变成了新的树根,----对边权求和
                  ///到维护前树根的距离+维护前树根到新树根的距离
    return fa[x]=root;

}
void mergefa(int a,int b)
{
    fa[getfa(a)]=getfa(b);
}
void mergefa(int a,int b)///之前提到了需要+维护前树根到新树根的距离
{                           ///那么这个距离需要两个步骤

    d[getfa(a)]=fasize(getfa(b));///1.接到尾部,旧树根的边权d[x](到新树根距离)就是目标树的size(边数)
    fasize[get(fa(b))]+=fasize[getfa(b)];///2.成为新树,size变大
    fa[getfa(a)]=getfa(b);


}
int main()
{

    return 0;
}

2021.4.17研究出来的模板

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1005
int fa[maxn];
int d[maxn];
void initfa()
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }
}
int getfa(int x)
{
    if(fa[x]==x)return x;
    ///这两行就是维护权值数组
    int root=fa[x];
    d[x]+=d[fa[x]];
    
    return fa[x]=root;
}
void mergefa(int x,int y)
{
    ///这行是方便使用
    rx=getfa(x);ry=getfa(y);
    ///这两行是维护权值数组
    d[rx]=d[ry]+1;//d[rx]=size(y);
    sizem[y]+=sizem[x];
    ///这行是原来的
    fa[x]=ry;

}


}
int main()
{

    return 0;
}


奇偶游戏

在这里插入图片描述

边权集解法

先将正常数组改成前缀和,变成两个点的奇偶性,这样“权”就可以变成d[x](x与根之间),奇偶性的话,就用抑或来计算,如果已经有了,那么通过dx来计算x与y之间奇偶性,如果没有联通,那么要通过已知x与y之间奇偶性,倒推两个根节点之间的奇偶性,合并,另外数很稀疏,考虑离散化

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

acwing秦淮河大佬的复现书上代码,思路很棒
#include<bits/stdc++.h>
using namespace std;
const int N=10010<<1;
struct node
{
    int l,r,ans;
} q[N];
int a[N],fa[N],d[N],n,m,t_n;
int get(int x)
{
    if (x==fa[x])
        return x;
    int root=get(fa[x]);
    d[x]^=d[fa[x]];//异或
    return fa[x]=root;
}
inline int read_init()//离散化
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        char str[5];
        scanf("%d%d%s",&q[i].l,&q[i].r,str);
        q[i].ans=(str[0]=='e'?0:1);
        a[++t_n]=q[i].l-1;
        a[++t_n]=q[i].r;
    }
    sort(a+1,a+1+t_n);
    n=unique(a+1,a+1+t_n)-a-1;//去重
}
inline int work()
{
    read_init();
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=lower_bound(a+1,a+1+n,q[i].l-1)-a;//离散化后要找数
        int y=lower_bound(a+1,a+1+n,q[i].r)-a;
        int p=get(x),q2=get(y);
        if (p==q2)
        {
            if ((d[x]^d[y])!=q[i].ans)//变量要相等,但是却不相等了.
            {
                cout<<i-1<<endl;
                return 0;
            }
        }
        else
        {
            fa[p]=q2;//合并merge.两方代码就懒得写函数了,见谅
            d[p]^=d[x]^d[y]^q[i].ans;//统统异或
        }
    }
    cout<<m;//数据过于优秀,一个问题都没有
}
int main()
{
    work();
    return 0;
}

扩展域解法(这个很简单,维护两个域,互相也是联通的,不用算权值了)

在这里插入图片描述
在这里插入图片描述

//Wan Hong 3.0
//notebook
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>

typedef long long ll;
typedef std::pair<ll,ll> pll;
ll read()
{
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')f=-1;
        else c=getchar();
    }
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return f*x;
}
ll min(ll a,ll b)
{
    return a<b?a:b;
}
ll max(ll a,ll b)
{
    return a>b?a:b;
}
bool umin(ll &a,ll b)
{
    if(b<a)return a=b,1;
    return 0;
}
bool umax(ll &a,ll b)
{
    if(b>a)return a=b,1;
    return 0;
}
ll abs(ll x)
{
    return x>0?x:-x;
}
/**********/
#define MAXN 100011
struct ufs
{
    ll fa[MAXN];
    void build(ll n)
    {
        for(ll i=0;i<=n;++i)fa[i]=i;
    }
    ll find(ll x)
    {
        if(fa[x]==x)return x;
        return fa[x]=find(fa[x]);
    }
    void uni(ll u,ll v)
    {
        u=find(u),v=find(v);
        fa[u]=v;
    }
    bool same(ll u,ll v)
    {
        return find(u)==find(v);
    }
}s;
struct query
{
    ll l,r,k;
    query(){}
    query(ll _l,ll _r,ll _k)
    {
        l=_l,r=_r,k=_k;
    }
}q[MAXN];
ll a[MAXN];
ll cnt=0;
void disc()
{
    std::sort(a+1,a+cnt+1);
    cnt=std::unique(a+1,a+cnt+1)-(a+1);
}
ll place(ll x)
{
    return std::lower_bound(a+1,a+cnt+1,x)-a;
}
int main()
{
    ll n=read(),m=read();
    for(ll i=1;i<=m;++i)
    {
        ll l=read()-1,r=read();
        char c=getchar();
        while(c!='e'&&c!='o')c=getchar();
        q[i]=query(l,r,c=='o');
        a[++cnt]=l,a[++cnt]=r;
    }
    disc();
    s.build(cnt<<1);//[1,cnt]:x_even;[cnt+1,2cnt]:x_odd
    for(ll i=1;i<=m;++i)
    {
        ll l=place(q[i].l),r=place(q[i].r),k=q[i].k;
        ll l_even=l,l_odd=l+cnt,r_even=r,r_odd=r+cnt;
        if(!k)
        {
            if(s.same(l_odd,r_even)||s.same(l_even,r_odd))
            {
                printf("%lld\n",i-1);
                return 0;
            }
            else s.uni(l_odd,r_odd),s.uni(l_even,r_even);
        }
        else
        {
            if(s.same(l_even,r_even)||s.same(l_odd,r_odd))
            {
                printf("%lld\n",i-1);
                return 0;
            }
            else s.uni(l_even,r_odd),s.uni(l_odd,r_even);
        }
    }
    printf("%lld",m);
    return 0;
}

作者:whsstory
链接:https://www.acwing.com/solution/content/4338/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

扩展域解法1.不要忘了初始化2.不要忘了-1 3.扩展域一定要放一个数组里 ------------2021.3.8复习

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100005
int fa[maxn];///扩展域应该放在同一个数组里使用,x,x+n,x+n+n,x+n+n+n,......这样才能测试是否联通
int d[maxn];
int sizefa[maxn];
void initfa()
{
    for(int i=0;i<maxn;i++)
    {
        fa[i]=i;
    }

}
int getfa(int x)
{
    if(fa[x]==x)return x;

    return fa[x]=getfa(fa[x]);
}
void mergefa(int a,int b)
{
    fa[getfa(a)]=getfa(b);
}
int main()
{
    initfa();
    int n;
    cin>>n;

    int t;
    cin>>t;
    int t2=t;
    int a,b,op;
    while(t--)
    {
        cin>>a>>b>>op;

        if(op==0)///even
        {
            if(getfa(a-1)==getfa(b+n))///is odd--矛盾
            {
                cout<<t2-t<<'*';
                return 0;
            }
            mergefa(a-1,b);
            mergefa(a-1+n,b+n);
        }
        else{

            if(getfa(a-1)==getfa(b))
            {
                cout<<t2-t<<"#";
                return 0;
            }
            mergefa(a-1,b+n);
            mergefa(a-1+n,b);
        }

    }
    return 0;
}


食物链

在这里插入图片描述

拓展域

三个域

在这里插入图片描述

//这里我们将三个域,分别转化为了n,n+n,n+n+n.因为读入方面特别烦.
#include <bits/stdc++.h>
using namespace std;
int fa[200000];
int n,m,k,x,y,ans;
int get(int x)
{
    if(x==fa[x])
        return x;
    return fa[x]=get(fa[x]);
}
void merge(int x,int y)
{
    fa[get(x)]=get(y);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=3*n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&k,&x,&y);
        if(x>n || y>n)
            ans++;///假话总数+1
        else if(k==1)///同类
        {
            if(get(x)==get(y+n) || get(x)==get(y+n+n))
            //如果x,y是同类,但是x是y的捕食中的动物,或者x是y天敌中的动物,那么错误.
                ans++;///假话总数+1
            else
            {
                merge(x,y);
                merge(x+n,y+n);
                merge(x+n+n,y+n+n);
            }
        }
        else///X捕食Y
        {
            if(x==y || get(x)==get(y) || get(x)==get(y+n))
            //x就是y,或者他们是同类,再或者是y的同类中有x
                ans++;//都是假话
            else
            {
                merge(x,y+n+n);//y的天敌域加入x
                merge(x+n,y);//x的捕食域加入y
                merge(x+n+n,y+n);//x的天敌域是y的捕食域.
            }
        }
    }
    cout<<ans<<endl;
}
//x是同类域.
//x+n是捕食域
//x+n+n是天敌域

2021.3.8复习:这里的捕食域,同类域,天敌域,通过三个域表达出了方向关系(X被Y捕食,Y是X的天敌):通过X的Xself 与 Y的 Yeat 联通

边带权

把吃,被吃,同类设为 1,2,0,每次维护就好
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 233;
int fa[maxn], d[maxn];
int ff(int x)
{
    if(fa[x] == x) return x;
    int r = ff(fa[x]);
    d[x] += d[fa[x]];
    return fa[x] = r;
}
int main()
{
    int n,k; cin >> n >> k;
    for(int i = 0; i <= n; i++) fa[i] = i;
    int ans = 0;
    for(int i = 1; i <= k; i++)
    {
        int t, a, b;
        scanf("%d%d%d", &t, &a, &b);
        if(a > n || b > n) {ans ++; continue;}
        else if(t == 2 && a == b) {ans++; continue;}
        else
        {
            int rel;
            if(t == 2) rel = 1;
            else rel = 0;
            int x = ff(a), y = ff(b);
            if(x == y) 
            {
                if((((d[a] - d[b]) % 3) + 3) % 3 != rel)
                ans++;
            }
            else
            {
                fa[x] = y;
                d[x] = d[b] - (d[a] - rel);
            }
        }
    }
    cout<< ans;
}

作者:这个显卡不太冷
链接:https://www.acwing.com/solution/content/1357/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值