考点难度都很合适的一套题目,大概在day1到day2之前
T1
猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有N个苹果,每个苹果有一个编号,分
别为0~N-1,它们之间由N-1个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可
以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置。猴猴开始在编号为K的苹果的位
置,并且把这个苹果吃了,之后每一天猴猴都要去吃一个苹果,但是树上那么多苹果吃哪个呢?
猴猴想到自己去吃苹果时一定会把路上遇到的苹果都吃掉,于是猴猴决定去吃能让自己这天吃的
苹果数量最多的那个苹果,如果有多个苹果满足条件,猴猴就会去吃这些中编号最小的苹果,那
么猴猴会按照什么顺序吃苹果呢?
对于30%的数据:N<=100
对于60%的数据:N<=1000
对于100%的数据:N<=50000,0<=K<N
分析:60%的分数都只要暴力模拟就好了,每次找最长的走过去;
考虑一个性质:走过的点的权值会消失,那么每次移动到终点位置和每次都从初始给定的点出发的答案都一样的,而且终点一定会是叶子节点;
那么我们设初始节点为根节点,先进行一遍dfs,把所有叶子节点记录下来,按照深度排序;
然后让每个点沿着父亲往上跳,每跳一步答案增加1,遇到根节点或者走过的节点就停止(遇到走过的点说明上面的权值都没有了
然后把叶子节点按答案从小到大输出(看完题解觉得智商被人按在地上摩擦了)
#include<bits/stdc++.h> using namespace std; namespace knife_rose{ #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) inline int read() { int x=0;char ch,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } const int N=1e5+10; int n,rt; int head[N],cnt; struct point { int nxt,to; point(){} point(const int &nxt,const int &to):nxt(nxt),to(to){} }a[N<<1]; inline void link(int x,int y) { a[++cnt]=(point){head[x],y};head[x]=cnt; a[++cnt]=(point){head[y],x};head[y]=cnt; } int f[N],dep[N],lea[N],num,sum[N]; bool vis[N]; inline void dfs(int now,int fa) { f[now]=fa; dep[now]=dep[fa]+1; bool flag=0; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; flag=1; dfs(t,now); } if(!flag) lea[++num]=now; } inline bool cmp1(int a,int b)//深度排序,深度相同按编号大小 { return dep[a]^dep[b]?dep[a]>dep[b]:a<b; } inline bool cmp2(int a,int b)//答案排序 { return sum[a]^sum[b]?sum[a]>sum[b]:a<b; } signed main() { n=read(),rt=read(); for(int x,i=1;i<n;++i) { x=read(); link(x,i); } dfs(rt,rt); sort(lea+1,lea+num+1,cmp1); vis[rt]=1; for(int i=1;i<=num;++i) { int now=lea[i]; while(!vis[now]) { ++sum[lea[i]]; vis[now]=1; now=f[now]; } } sort(lea+1,lea+num+1,cmp2); printf("%d ",rt); for(int i=1;i<=num;++i) printf("%d ",lea[i]); return 0; } } signed main() { return knife_rose::main(); }
T2
猴猴最爱吃香蕉了。每天猴猴出门都会摘很多很多的香蕉,每个香蕉都有一个甜度,猴猴不一定
要把所有的香蕉都吃掉,猴猴每天都有一个心情值K,猴猴希望当天吃的香蕉满足这么一个条件,
这些香蕉的甜度乘积恰好等于K,但是猴猴并不知道有多少种方法,于是猴猴把这个问题交给你。
对于30%的数据:n,K<=100
对于60%的数据:n<=1000,K<=10000
对于100%的数据:n<=1000,K<=100000000,D<=20(D是数据组数
30%:我也没想出来咋拿30分(
60%:题目是个很明显的背包,但是由于是乘法所以有一些特殊之处:只有k的约数才能作为转移,所以我们可以筛出来k的约数,是根号量级的;
统计答案需要二分查找,复杂度O(D×n√k×log(√k))
100%:其实上面那个做法就是100分,然而官方题解也是这么给的,但是很明显复杂度不对啊qwq
然而机房里某位巨佬想出了复杂度更优秀的做法,%%%szx
先对k进行质因数分解,根据每个质因子个数将答案压缩成一个多进制状态,然后将每个a[i]也压缩成对应的多进制状态进行转移,可以发现质因子个数很少,不超过30个
设t是多进制状态最大值
复杂度为O(√k+n√a[i]+nt)
#include<bits/stdc++.h> using namespace std; namespace knife_rose{ #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) inline int read() { int x=0;char ch,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } const int N=1e4+10,p=1e9+7; int tyx,n,m,ed,tot; int a[N]; int st[40],sum[40],top; int git[40],c[40],s[N][40]; int g[N],f[1000010]; bool vis[N]; inline void clear() { memset(g,0,sizeof(g)); memset(f,0,sizeof(f)); memset(sum,0,sizeof(sum)); memset(s,0,sizeof(s)); memset(vis,0,sizeof(vis)); top=0; } signed main() { tyx=read(); while(tyx--) { clear(); n=read(),m=read(); for(int i=1;i<=n;++i) a[i]=read(); int qr=sqrt(m),tmp=m; for(int i=2;i<=qr;++i) { if(tmp%i==0) { st[++top]=i; while(tmp%i==0) ++sum[top],tmp/=i;//质因数分解 } } if(tmp^1) st[++top]=tmp,sum[top]=1;//加上大质因子 git[1]=1; for(int i=1;i<=top;++i) git[i+1]=git[i]*(sum[i]+1);//git[i]表示,多进制状态下,第i位一个1代表十进制多少 ed=0; for(int i=1;i<=top;++i) ed+=git[i]*sum[i];//ed是最终状态 for(int i=1;i<=n;++i) { if(m%a[i]){vis[i]=1;continue;}//不是M的约数没用 qr=sqrt(a[i]),tot=0; for(int j=2;j<=qr;++j) { if(a[i]%j==0) { c[++tot]=j; while(a[i]%j==0) ++s[i][tot],a[i]/=j;//质因数分解*2 } } if(a[i]^1) c[++tot]=a[i],s[i][tot]=1; int t=1; for(int j=1;j<=top;++j) { while(c[t]<st[j]&&t<tot) ++t; if(c[t]==st[j]) g[i]+=git[j]*s[i][t];//g[i]为a[i]对应的多进制状态数 } } f[0]=1; for(int t,now,flag,i=1;i<=n;++i) { if(vis[i]) continue; for(int j=ed;~j;--j) { flag=0; t=0; for(int k=1;k<=top;++k) { now=(j%git[k+1])/git[k];//now是a[i]对应的g[i]在第i位有多大 if(now+(g[i]%git[k+1])/git[k]>sum[k]){flag=1;break;}//如果第i位当前数字+g[i]的第i位数字大于上界取消 t+=(now+(g[i]%git[k+1])/git[k])*git[k]; } if(flag) continue; f[t]+=f[j]; if(f[t]>=p) f[t]-=p; } } printf("%d ",f[ed]); } return 0; } } signed main() { return knife_rose::main(); } /* 1 10 10 1 5 1 2 4 5 1 2 1 5 */
T3
猴猴今天要和小伙伴猩猩比赛爬树,为了公平不碰撞,猴猴和猩猩需要在不同的树上攀爬。于是
它们选了两颗节点数同为n的树,并将两棵树的节点分别以1~n标号(根节点标号为1),但两棵树
的节点连接方式不尽相同。
现在它们决定选择两个标号的点进行比赛。为了方便统计,规定它们比赛中必须都向上爬。(即
选定的赛段节点u→节点v都必须指向叶子方向)请你求出这两棵树上共有多少对节点满足比赛的需
求。
对于30%的数据:n≤1000
对于50%的数据:n≤10000
对于100%的数据:n≤100000,1≤a,b≤n
题意大概是选一对点使得在这两颗树上都满足某个点是另一个点的祖先
30%:记录每个点是不是在两棵树上都是自己祖先,枚举点对
50%:???好像没啥办法
100%:其实这个题目就是一个树上逆序对问题,我们考虑先对第一颗树以dfs序为下标建树状数组,再遍历第二棵树,每次进去的先把自己放进树状数组,然后求一下当前子树范围内点的个数,出来的时候再求一遍,两次结果作差就是满足题目要求的点对(进去的时候不存在而回溯的时候存在说明既是第一颗树的子树也是第二棵树的子树)
#include<bits/stdc++.h> using namespace std; namespace knife_rose{ #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) inline int read() { int x=0;char ch,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } const int N=1e5+10; int n,ret; int head[N],cnt; struct point { int nxt,to; point(){} point(const int &nxt,const int &to):nxt(nxt),to(to){} }a[N<<1]; inline void link(int x,int y) { a[++cnt]=(point){head[x],y};head[x]=cnt; a[++cnt]=(point){head[y],x};head[y]=cnt; } int st[N],ed[N],idx; inline void dfs1(int now,int fa) { st[now]=++idx; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs1(t,now); } ed[now]=idx; } int tr[N]; inline int lowbit(int i) { return i&-i; } inline void update(int x,int k) { for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k; } inline int query(int y) { int ret=0; for(int i=y;i;i^=lowbit(i)) ret+=tr[i]; return ret; } inline void dfs2(int now,int fa)//树上逆序对 { int ans=query(ed[now])-query(st[now]-1); for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs2(t,now); } ret+=query(ed[now])-query(st[now]-1)-ans; update(st[now],1); } signed main() { n=read(); for(int x,y,i=1;i<n;++i) { x=read(),y=read(); link(x,y); } dfs1(1,0); memset(head,0,sizeof(head)); cnt=0; for(int x,y,i=1;i<n;++i) { x=read(),y=read(); link(x,y); } dfs2(1,0); printf("%d ",ret); return 0; } } signed main() { return knife_rose::main(); }