题目:
题解:
这题一眼点分治然而并不是点分治
暴力分较多。
算法一
首先这边权肯定不是让你乘起来的,连个模数都不给就连边权都是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);
}