poj1741树上的点对
题目大意:给定一棵树,求树上距离不超过k的点对个数。
思路:点分治模板题。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> #define maxnode 10005 #define inf 2100000000LL using namespace std; int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},va[maxnode*2]={0}, siz[maxnode]={0},tot=0,maxn,root,ans=0,k,n; bool visit[maxnode]={0}; vector<int> dis; void add(int u,int v,int w) { ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w; ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w; } void getroot(int u,int fa,int nn) { int i,maxsiz=0,j; siz[u]=1; for (i=point[u];i;i=next[i]) { if (!visit[j=en[i]]&&j!=fa) { getroot(j,u,nn);siz[u]+=siz[j]; maxsiz=max(maxsiz,siz[j]); } } maxsiz=max(maxsiz,nn-siz[u]); if (maxsiz<maxn) { maxn=maxsiz;root=u; } } void getdep(int u,int dep,int fa) { int i; dis.push_back(dep);siz[u]=1; for (i=point[u];i;i=next[i]) if (en[i]!=fa&&!visit[en[i]]) { getdep(en[i],dep+va[i],u);siz[u]+=siz[en[i]]; } } int cal(int u,int dep) { int sum=0,i,j; dis.clear();getdep(u,dep,0); sort(dis.begin(),dis.end()); for (i=0,j=dis.size()-1;i<j;) { if (dis[i]+dis[j]<=k) { sum+=j-i;++i; } else --j; } return sum; } void work(int u) { int i,j; ans+=cal(u,0);visit[u]=true; for (i=point[u];i;i=next[i]) { if (!visit[en[i]]) { ans-=cal(en[i],va[i]); maxn=siz[en[i]]; getroot(en[i],root=0,siz[en[i]]); work(root); } } } int main() { freopen("poj1741_tree.in","r",stdin); freopen("poj1741_tree.out","w",stdout); int i,j,u,v,w; while(scanf("%d%d",&n,&k)==2) { if (n==0&&k==0) break; tot=0;memset(visit,false,sizeof(visit)); memset(point,0,sizeof(point)); for (i=1;i<n;++i) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } maxn=n;ans=0; getroot(1,root=0,n); work(root); printf("%d\n",ans); } }
hdu4670Cube number on a tree
题目大意:给定一棵树,每个点都有一个权值,可以分为一些给定质数的和(质数个数不超过30),求出一些路径的条数,使路径都满足路上点权乘积是个立方数。
思路:我们对于每一个点都分解质因数,保存下每种的个数,满足条件的路径就是那些点的相应因数个数相加后%3=0的路径了。
点分,对每一个点统计路径个数,减去重复的就可以了。
对于统计答案的部分,我们dfs求出到点到重心路径上的和,把这个30位的数组转成一个三进制数,放到map里。穷举每一个map里面的数,找到互补的数,贡献给答案就可以了。这里注意如果这个数和互补数一样的话,ans的计算方式略有不同。
对于重复的部分,我们减去的时候要时刻注意变量的含义。
这道题目的思维和代码量都中等,但是结合起来莫名的很难写(蒟蒻的吐槽),要认真思考好每一个部分的实现。
#pragma comment(linker,"/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define maxnode 50005 #define inf 2100000000LL #define sta 30 using namespace std; struct use{ int num[35]; void init() { int i; for (i=0;i<35;++i) num[i]=0; } }ai[maxnode]={0}; int prime[35]={0},point[maxnode]={0},nex[maxnode*2]={0},en[maxnode*2]={0},tot=0, root,son[maxnode]={0},siz[maxnode]={0}; long long ans=0,mi[35]={0}; bool visit[maxnode]={false}; map <long long,long long> cnt; void add(int u,int v) { ++tot;nex[tot]=point[u];point[u]=tot;en[tot]=v; ++tot;nex[tot]=point[v];point[v]=tot;en[tot]=u; } void fp(int j,long long x) { int i; ai[j].init(); for (i=1;i<=prime[0];++i) { while(x!=0&&x%prime[i]==0) { x/=prime[i];++ai[j].num[i]; if (ai[j].num[i]>=3) ai[j].num[i]%=3; } } } long long threezten(use x) { int i,j; long long sum=0; for (i=1;i<=sta;++i) sum+=mi[i-1]*(x.num[i]%3); return sum; } use tenzthree(long long x) { int i,j; use y; y.init();i=1; while(x) { y.num[i]=x%3;x/=3;++i; } return y; } use jia(use x,use y) { int i; for (i=1;i<=sta;++i) x.num[i]=(x.num[i]+y.num[i])%3; return x; } use jian(use x,use anc) { int i; use y; for (i=1;i<=sta;++i) y.num[i]=(3-x.num[i]+anc.num[i])%3; return y; } bool equal(use x,use y) { int i; for (i=1;i<=sta;++i) if (x.num[i]!=y.num[i]) return false; return true; } void getroot(int u,int fa,int up) { int i,j; siz[u]=1;son[u]=0; for (i=point[u];i;i=nex[i]) { if ((j=en[i])!=fa&&!visit[j]) { getroot(j,u,up);siz[u]+=siz[j]; son[u]=max(son[u],siz[j]); } } son[u]=max(son[u],up-siz[u]); if (son[u]<son[root]) root=u; } void getdis(int u,int fa,use dis) { int i,j; long long kk; if (!cnt.count(kk=threezten(dis))) cnt[kk]=0; ++cnt[kk];siz[u]=1; for (i=point[u];i;i=nex[i]) { if ((j=en[i])!=fa&&!visit[j]) { getdis(j,u,jia(dis,ai[j]));siz[u]+=siz[j]; } } } long long calc(int u,use cc,use anc) { int i,j; long long sum=0,kk; use x,y; cnt.clear(); getdis(u,0,jia(cc,ai[u])); map<long long,long long>::iterator it; for (it=cnt.begin();it!=cnt.end();++it) { if (it->second!=0) { x=tenzthree(it->first);y=jian(x,anc); if (cnt.count(kk=threezten(y))) { if (equal(x,y)) sum+=(cnt[kk]+cnt[kk]*(cnt[kk]-1)/2); else sum+=((it->second)*(cnt[kk])); cnt[kk]=0;it->second=0; } } } return sum; } void work(int u) { int i,j; use kk; kk.init();visit[u]=true; ans+=calc(u,kk,ai[u]); for (i=point[u];i;i=nex[i]) { if (!visit[j=en[i]]) { ans-=calc(j,ai[u],ai[u]); root=0;son[root]=inf; getroot(j,0,siz[j]); work(root); } } } int main() { int n,i,j,k,u,v; long long aa; mi[0]=1; for (i=1;i<=sta;++i) mi[i]=mi[i-1]*3; while(scanf("%d%d",&n,&prime[0])==2) { ans=tot=0; memset(point,0,sizeof(point)); memset(nex,0,sizeof(nex)); memset(visit,false,sizeof(visit)); for (i=1;i<=prime[0];++i) scanf("%d",&prime[i]); for (i=1;i<=n;++i) { scanf("%I64d",&aa);fp(i,aa); } for (i=1;i<n;++i) { scanf("%d%d",&u,&v);add(u,v); } root=0;son[root]=inf;getroot(1,0,n); work(root); printf("%I64d\n",ans); } }
bzoj2152聪聪可可
题目大意:给定一棵树,求边权和是三的倍数的路径数(同一个点和正反路径都算不同的路径),以分数的形式表示出来(分母是总的路径数n^2)。
思路:点分,用0、1、2为下标保存路径个数,然后相应的乘起来就可以了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxedge 20005 #define maxnode 20005 using namespace std; int point[maxedge*2]={0},next[maxedge*2]={0},en[maxedge*2]={0},siz[maxnode]={0},va[maxedge*2]={0}, ans=0,root,maxn,tot=0,zhan[3]={0},dis[maxnode]={0}; bool visit[maxnode]={false}; int gcd(int x,int y) { if (!y) return x; else return gcd(y,x%y); } void add(int x,int y,int w) { ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w; ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w; } void getroot(int u,int fa,int al) { int i,j,maxsiz=0; siz[u]=1; for (i=point[u];i;i=next[i]) { if ((j=en[i])!=fa&&!visit[j]) { getroot(j,u,al);siz[u]+=siz[j]; maxsiz=max(maxsiz,siz[j]); } } maxsiz=max(maxsiz,al-siz[u]); if (maxsiz<maxn){maxn=maxsiz;root=u;} } void getdep(int u,int fa,int dep) { int i,j; dis[u]=dep;siz[u]=1;++zhan[dep]; for (i=point[u];i;i=next[i]) { if (!visit[j=en[i]]&&j!=fa) { getdep(j,u,(dep+va[i])%3);siz[u]+=siz[j]; } } } int calc(int u,int al) { int i,j,sum=0; memset(zhan,0,sizeof(zhan)); getdep(u,0,al); sum+=zhan[0]*zhan[0]+2*zhan[1]*zhan[2]; return sum; } void work(int u) { int i,j; ans+=calc(u,0);visit[u]=true; for (i=point[u];i;i=next[i]) { if (!visit[j=en[i]]) { ans-=calc(j,va[i]%3);maxn=siz[j]; getroot(j,root=0,siz[j]); work(root); } } } int main() { int i,j,n,x,y,w,mu,k; scanf("%d",&n);mu=n*n; for (i=1;i<n;++i) { scanf("%d%d%d",&x,&y,&w); add(x,y,w); } maxn=n;getroot(1,root=0,n);work(root); k=gcd(mu,ans);mu/=k;ans/=k; printf("%d/%d\n",ans,mu); }
bzoj4016最短路径树问题
题目大意:给定一个无向图,求最短路径树(从1开始且每个点到根的路径字典序最小),然后求最短路径树上的经过k个点的最长路径以及经过k个点的长度为最长路径长度的路径个数。
思路:用点分,在更新最长路径的时候更新一下方案数,注意不能简单的+1-1,而是一些累加。
一开始理解错了题意(理解成了路径长度为最长路径长度,但经过点的个数不一定是k的路径个数),写了两个点分wa+tle。。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #define maxnode 30005 #define maxedge 60005 #define len 100000 using namespace std; struct use{ int dian,dx; }; int poi[maxnode]={0},nex[maxedge*2]={0},enn[maxedge*2]={0},vaa[maxedge*2]={0},to=0,k,dis[maxnode]={0},que[len+5]={0},ans=0,root={0}, point[maxnode]={0},next[maxedge*2]={0},en[maxedge*2]={0},va[maxedge*2]={0},tot=0,siz[maxnode]={0},maxn,n,kk=0,zhan[maxnode][2]={0}; bool visit[maxnode]={false},flag[maxedge*2]={false}; void add1(int x,int y,int w) { ++to;nex[to]=poi[x];poi[x]=to;enn[to]=y;vaa[to]=w; ++to;nex[to]=poi[y];poi[y]=to;enn[to]=x;vaa[to]=w; } void add(int x,int y,int w) { ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w; ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w; } void spfa() { int x,y,i,j,tail,head; memset(dis,127,sizeof(dis));dis[1]=0;visit[1]=true; head=tail=0;que[++tail]=1; while(head!=tail) { head=head%len+1; x=que[head];visit[x]=false; for (y=poi[x];y;y=nex[y]) { if (dis[enn[y]]>dis[x]+vaa[y]) { dis[enn[y]]=dis[x]+vaa[y]; if (!visit[enn[y]]) { visit[enn[y]]=true;tail=tail%len+1; que[tail]=enn[y]; } } } } for (i=1;i<=n;++i) for (j=poi[i];j;j=nex[j]) if (dis[enn[j]]==dis[i]+vaa[j]) flag[j]=true; } int my_comp(const use x,const use y) { if (x.dian<y.dian) return 1; else return 0; } void build(int u,int fa,int dd) { int i,j; vector<use> cc; use xx; cc.clear();visit[u]=true; if (fa) add(fa,u,dd); for (i=poi[u];i;i=nex[i]) if (flag[i]) {xx.dian=enn[i];xx.dx=vaa[i];cc.push_back(xx);} sort(cc.begin(),cc.end(),my_comp); for (i=0;i<cc.size();++i) if (!visit[cc[i].dian]) build(cc[i].dian,u,cc[i].dx); } void getroot(int u,int fa,int al) { int i,j,maxsiz=0; siz[u]=1; for (i=point[u];i;i=next[i]) { if (!visit[j=en[i]]&&j!=fa) { getroot(j,u,al);siz[u]+=siz[j]; maxsiz=max(maxsiz,siz[j]); } } maxsiz=max(maxsiz,al-siz[u]); if (maxsiz<maxn){root=u;maxn=maxsiz;} } void getdep(int u,int fa,int dep,int dd) { int i,j; dis[u]=dep; if (k-1-dd>=0) { if ((j=dis[u]+zhan[k-1-dd][0])>=kk) { if (j==kk) ans+=zhan[k-1-dd][1]; else {ans=zhan[k-1-dd][1];kk=j;} } } for (i=point[u];i;i=next[i]) if (!visit[j=en[i]]&&j!=fa) getdep(j,u,dep+va[i],dd+1); } void ins(int u,int fa,int dep,int dd) { int i,j; siz[u]=1;dis[u]=dep; if (zhan[dd][0]<=dis[u]) { if (zhan[dd][0]<dis[u]) {zhan[dd][0]=dis[u];zhan[dd][1]=1;} else ++zhan[dd][1]; } for (i=point[u];i;i=next[i]) if (!visit[j=en[i]]&&j!=fa) { ins(j,u,dep+va[i],dd+1);siz[u]+=siz[j]; } } void workk(int u) { int i,j,x,y; visit[u]=true;memset(zhan,0,sizeof(zhan));zhan[0][0]=0;zhan[0][1]=1; for (i=point[u];i;i=next[i]) { if(!visit[j=en[i]]){getdep(j,0,va[i],1);ins(j,0,va[i],1);} } for (i=point[u];i;i=next[i]) { if (!visit[j=en[i]]) { maxn=siz[j];getroot(j,root=0,siz[j]);workk(root); } } } void getk() { int i,j,x,y; memset(visit,false,sizeof(visit)); maxn=n;getroot(1,root=0,n);workk(root); } int main() { int m,i,j,t,x,y,w; scanf("%d%d%d",&n,&m,&k); for (i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&w); add1(x,y,w); } spfa();memset(visit,false,sizeof(visit)); build(1,0,0);getk(); printf("%d %d\n",kk,ans); }