【Enzymii的胡策】互测3.25 T1(质因数分解+dfs+hash)

本文详细解析了点分治算法的三种实现方式,包括通过质因数分解进行判断、使用二进制压缩来处理较小边权的情况以及通过哈希方法解决大范围质因数的问题。这些方法有助于解决特定类型的图论问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是T1

题解:

这题一眼点分治然而并不是点分治
暴力分较多。

算法一

首先这边权肯定不是让你乘起来的,连个模数都不给就连边权都是2也要炸longlong无疑。判断完全平方的方法可以是所有质因子出现个数都是偶数,那我们的算法1就是对于每条边分解质因数,dfs判断。因为在1e8之内的质因子最多是2*3*5*7*11*13*17*19*23=223092870,不会超过10个,这个算法的时间复杂度是 O(n2) O ( n 2 ) 带一堆常数,30pts是可以的,但是5000*5000就过不去了,注意cnt数组是不同的质因子个数,应该开到1e8之内的质因子个数。(但是我是瞎给的。)

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=4100;
int tot,nxt[N*2],point[N],v[N*2],fj[N*2][15],maxn,cnt[30000],ans,vis[N];
map<int,int>cx,dj;
void fenjie(int x,int z)
{
    for (int i=2;i*i<=z;i++)
      if (z%i==0)
      {
        int num=0;
        while (z%i==0) num++,z/=i;
        if (num%2) fj[x][++fj[x][0]]=i;
      }
    if (z!=1) fj[x][++fj[x][0]]=z;
}
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; fenjie(tot,z);
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
    memcpy(fj[tot],fj[tot-1],sizeof(fj[tot]));
}
bool check()
{
    for (int i=1;i<=maxn;i++)
      if (cnt[i]) return 0;
    return 1;
}
void dfs(int x,int ct)
{
    if (x!=ct && check()) ans++;
    vis[x]=ct;
    for (int i=point[x];i;i=nxt[i])
      if (vis[v[i]]!=ct)
      {
        for (int j=1;j<=fj[i][0];j++)
        {
            int num=fj[i][j];
            if (!cx[num]) dj[num]=++maxn;
            cx[num]++;
            cnt[dj[num]]^=1;
        }
        dfs(v[i],ct);
        for (int j=1;j<=fj[i][0];j++)
        {
            int num=fj[i][j];
            cnt[dj[num]]^=1;
            cx[num]--;
            if (!cx[num]) dj[num]=0;
        }
      }
}
int main()  
{
    freopen("eromanga.in","r",stdin);
    freopen("eromanga.out","w",stdout);
    int n;scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        addline(x,y,z);
    }
    for (int i=1;i<=n;i++) maxn=0,dfs(i,i);
    printf("%d",ans);
}

算法二

对于w≤1000的数据,我们可以把边权表示为一个二进制(状压,如果一个质因子出现了奇数次,那么该位上是1)
因此,一条路径的权值就是每条边权值的异或,权值为0就是完全平方数了
从根开始dfs求出根到每个结点的权值,那么u→v是可行的当且仅当根到u的权值等于根到v的权值(异或为0),把根到所有结点的权值排序统计一下即可,时间复杂度 O(nlogn) O ( n l o g n ) ,结合算法一,期望得分50。
下面是w<=1000的40pts

#include <cstdio>
#include <algorithm>
#define INF 1e9
#define LL long long
using namespace std;
const int N=131080;
int tot,nxt[N*2],point[N],v[N*2],size[N],f[N],sum,root,have[N],num,cnt[N],has[N],b[100005],sb;
LL ans,dis[N],c[N*2];bool vis[N];
void init()
{
    for (int i=2;i<=1000000;i++)
    {
        bool fff=1;
        for (int j=2;j*j<=i;j++)
          if (i%j==0) {fff=0;break;}
        if (fff) b[++sb]=i;
    }
}
void fenjie(int x,LL z)
{
    for (int i=1;i<=sb && b[i]<=z;i++)
      if (z%b[i]==0)
      {
        int num=0;
        while (z%b[i]==0) num++,z/=b[i];
        if (num%2) c[x]^=(1<<i-1);
      }
}
void addline(int x,int y,LL z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; fenjie(tot,z);
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=c[tot-1];
}
void dfs(int x,int fa)
{
    have[++num]=dis[x];
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa) dis[v[i]]=dis[x]^c[i],dfs(v[i],x);
}
int main()  
{
    freopen("eromanga.in","r",stdin);
    freopen("eromanga.out","w",stdout);
    init();
    int n;scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        int x,y;LL z;scanf("%d%d%lld",&x,&y,&z);
        addline(x,y,z);
    }
    dfs(1,0);
    sort(have+1,have+num+1);
    int ha=0,l=1,i;
    for (i=2;i<=num;i++) 
      if (have[i]!=have[i-1]) ans+=(LL)(i-l)*(LL)(i-l-1),l=i;
    ans+=(LL)(i-l)*(LL)(i-l-1);
    printf("%lld",ans);
}

算法三

考虑推广算法二,算法二的局限就在二进制压位上,如果我们预处理的话一异或就上天了(炸LL),其实因为二进制的原因1000也是承受不能的。
那我们就考虑把1∼10^8的每个质数hash成一个LL范围内的数, 然后像算法三一样从根一路异或即可
可以用随机法。

#include <map>
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;
const int N=151080;
int tot,nxt[N*2],point[N],v[N*2],num,sb,b[N];
LL ans,dis[N],c[N*2],have[N];bool ss[N];
map<int,LL>mp;
int read()
{
    int x=0;char ch=getchar();
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
void init()
{
    for (int i=2;i<=10000;i++)
    {
        if (!ss[i]) b[++sb]=i;
        for (int j=1;j<=sb && b[j]*i<=10000;j++)
        {
            ss[b[j]*i]=1;
            if (i%b[j]==0) break; 
        }
    }
}
LL neww()
{
    LL res=1000000000ll*rand()+rand();
    return res;
}
void fenjie(int x,int z)
{
    for (int i=1;i<=sb && b[i]<=z;i++)
      if (z%b[i]==0)
      {
        int num=0;
        while (z%b[i]==0) num++,z/=b[i];
        if (num%2) 
        {
            if (!mp[b[i]]) mp[b[i]]=neww();
            c[x]^=mp[b[i]];
        }  
      }
    if (z!=1) 
    {
        if (!mp[z]) mp[z]=neww();
        c[x]^=mp[z];
    }  
}
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; fenjie(tot,z);
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=c[tot-1];
}
void dfs(int x,int fa)
{
    have[++num]=dis[x];
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa) dis[v[i]]=dis[x]^c[i],dfs(v[i],x);
}
int main()  
{
    freopen("eromanga.in","r",stdin);
    freopen("eromanga.out","w",stdout);
    srand(3011221);
    init();
    int n;n=read();
    for (int i=1;i<n;i++)
    {
        int x,y,z;x=read();y=read();z=read();
        addline(x,y,z);
    }
    dfs(1,0);
    sort(have+1,have+num+1);
    int ha=0,l=1,i;
    for (i=2;i<=num;i++) 
      if (have[i]!=have[i-1]) ans+=(LL)(i-l)*(LL)(i-l-1),l=i;
    ans+=(LL)(i-l)*(LL)(i-l-1);
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值