test20190324 树
题意
树(tree.cpp/c/pas)
【题目背景】
这道题标算在评测机上的时间约为自己电脑的2/3
【问题描述】
【输入格式】
共 n+2 行。第 1 行 1 个数,n。
后面 2-n 行,每行两个数,x,y,表示树上的一条边。
第 n+1 行,n 个数,第 i 个数是 Ai。
第 n+2 行,n 个数,第 i 个数是 Bi。
【输出格式】
共一行,一个数,表示答案。
由于答案过大,只要求输出模 1e9+7(即 1000000007)后的的答案
【输入样例】
2
1 2
2 2
1 1
【输出样例】
8
【数据范围】
𝑛 ∈ [1,200000]
𝑥, 𝑦 ∈ [1, 𝑛],保证一定形成一个树
𝐴i ∈ [1,100000]
𝐵i ∈ [0,100000]
保证所有数字都是整数
第一个子任务 10%,满足n ∈ [1,5000]
第二个子任务 10%,满足数据纯随机
第三个子任务 20%,满足Bi ≠ 0
第四个子任务 20%,满足 Ai 没有除 1 以外平方因子
第五个子任务 40%,无特殊限制
分析
考场做法
后面那个取min显然是个容斥,用总的除掉取值都为0的路径的gcd就行了。
考虑如何求路径gcd乘积。考试的时候第一想法点分治,但是当时不会点分治,于是现学了一道题。又翻了翻资料,发现gcd的变动最多有log次。所以用map存一下gcd计数就好了。调了一会跑过了大样例,还挺快。后来想了想就这数据范围出题人要卡我的算法没有两把刷子是不可能的。
时间复杂度:玄学,但是实际跑出来跟std差不多。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=2e5+1,mod=1e9+7;
int n,a[N],b[N]={1};
vector<int> e[N];
int rt,sz=N,siz[N];
void search(int x,int fa){
siz[x]=1;
int t=0;
for(int i=0,y;i<e[x].size();++i){
if((y=e[x][i])==fa) continue;
search(y,x),siz[x]+=siz[y],t=max(t,siz[y]);
}
t=max(t,n-siz[x]);
if(t<sz) rt=x,sz=t;
}
int ans=1,fa[N];
map<int,int> c[N];
int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
int mul(int x,int y) {return (ll)x*y%mod;}
int fpow(int x,int k){
int re=1;
for(;k;k>>=1,x=mul(x,x))
if(k&1) re=mul(re,x);
return re;
}
void dfs1(int x,int fa){
::fa[x]=fa,c[x][a[x]]=1,ans=mul(ans,a[x]);
for(int i=0,y;i<e[x].size();++i){
if((y=e[x][i])==fa) continue;
dfs1(y,x);
for(map<int,int>::iterator i=c[y].begin();i!=c[y].end();++i)
for(map<int,int>::iterator j=c[x].begin();j!=c[x].end();++j)
ans=mul(ans,fpow(gcd(i->first,j->first),(ll)i->second*j->second%(mod-1)));
for(map<int,int>::iterator i=c[y].begin();i!=c[y].end();++i)
c[x][gcd(a[x],i->first)]+=i->second;
c[y].clear();
}
}
void dfs2(int x){
c[x][a[x]]=1,ans=mul(ans,fpow(a[x],mod-2));
for(int i=0,y;i<e[x].size();++i){
if((y=e[x][i])==fa[x]||b[y]) continue;
dfs2(y);
for(map<int,int>::iterator i=c[y].begin();i!=c[y].end();++i)
for(map<int,int>::iterator j=c[x].begin();j!=c[x].end();++j)
ans=mul(ans,fpow(fpow(gcd(i->first,j->first),(ll)i->second*j->second%(mod-1)),mod-2));
for(map<int,int>::iterator i=c[y].begin();i!=c[y].end();++i)
c[x][gcd(a[x],i->first)]+=i->second;
c[y].clear();
}
}
int main(){
freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
read(n);
for(int i=1,x,y;i<n;++i){
read(x),read(y);
e[x].push_back(y),e[y].push_back(x);
}
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<=n;++i) read(b[i]);
search(1,0);
dfs1(rt,0);
c[rt].clear();
for(int i=1;i<=n;++i)
if(!b[i]&&b[fa[i]]) dfs2(i);
printf("%d\n",ans);
return 0;
}
标解
实际上我问Z前辈,Z前辈给出的算法,就是标解。
题目大意:就是算哪个奇怪的式子
那个奇怪的式子差不多意思就是所有链gcd的积,要求链上至少有一个权值不为0
不用说了,真·送分题
先说部分分
第一个包:n^2爆一下
第二个包:随机数据,当然gcd大部分都是1,乱搞一下就好了
后面就直接是正解了
不同质因数分开来讨论
没了
权值的问题就是做两次,第二次把有权值的点断开,然后第一次答案除第二次就好了。
有平方因数就直接从高次向低次做
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define pi pair<int,int>
const int mod=1e9+7;
const int N=200005;
int pd[N],zs[N],tot,n,x,y,b[N];
int sz[N],kind[N],pos[N];
vector<int> e[N];
vector<pair<int,int> > a[N];
inline void read(int &x)
{
x=0;
char c=getchar();
while (c>'9'||c<'0')
c=getchar();
while (c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
}
inline int ksm(int x,int y)
{
int ans=1;
for (;y;y/=2,x=(ll)x*x%mod)
if (y&1)
ans=(ll)ans*x%mod;
return ans;
}
inline void get_zhishu()
{
pd[1]=1;
for (int i=2;i<=100000;++i)
{
if (!pd[i])
{
zs[++tot]=i;
pos[i]=tot;
}
for (int j=1;j<=tot&&zs[j]*i<=100000;++j)
{
pd[zs[j]*i]=1;
if (i%zs[j]==0)
break;
}
}
}
int fa[N];
inline int get_father(int x)
{
return fa[x]==x?x:fa[x]=get_father(fa[x]);
}
inline int solve()
{
int ans=1;
for (int i=1;i<=tot;++i)
if (a[i].size())
{
int sum=0;
for (unsigned j=0;j<a[i].size();++j)
if (!b[a[i][j].second])
{
pi tmp=a[i][j];
(sum+=tmp.first)%=(mod-1);
sz[tmp.second]=1;
kind[tmp.second]=i;
fa[tmp.second]=tmp.second;
for (unsigned k=0;k<e[tmp.second].size();++k)
{
int to=e[tmp.second][k];
if (kind[to]==i)
{
int u=get_father(tmp.second),v=get_father(to);
(sum+=(ll)sz[u]*sz[v]%(mod-1)*tmp.first%(mod-1))%=(mod-1);
fa[u]=v;
sz[v]+=sz[u];
}
}
}
ans=(ll)ans*ksm(zs[i],sum)%mod;
}
for (int i=1;i<=n;++i)
kind[i]=0;
return ans;
}
signed main()
{
get_zhishu();
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);
for (int i=1;i<n;++i)
{
read(x);
read(y);
e[x].push_back(y);
e[y].push_back(x);
}
for (int i=1;i<=n;++i)
{
read(x);
for (int j=1;j<=tot;++j)
{
int cnt=0;
while (x%zs[j]==0)
{
++cnt;
x/=zs[j];
}
if (cnt)
a[j].push_back(make_pair(cnt,i));
if ((ll)zs[j]*zs[j]>x)
break;
}
if (x!=1)
a[pos[x]].push_back(make_pair(1,i));
}
for (int i=1;i<=tot;++i)
if (a[i].size())
sort(a[i].begin(),a[i].end(),greater<pi>());
int ans=solve();
for (int i=1;i<=n;++i)
read(b[i]);
cout<<(ll)ans*ksm(solve(),mod-2)%mod;
return 0;
}