2024初三年前集训测试2
2024初三年前集训测试2
\(T1\) 上海 \(100pts\)
-
简化题意:给定一个正整数 \(k\) ,求最小的正整数 \(n\) 满足 \(k|n^{2},k \nmid n\) ,如无解则输出
-1
。- 类似 CF1444A Division 。
-
当 \(k=1\) 时,显然无解。
-
当 \(k \ne 1\) 时,由算术基本定理设 \(k=\prod\limits_{i=1}^{m}p_{i}^{c_{i}}\) ,容易得最小的正整数解 \(n\) 一定满足 \(n=\prod\limits_{i=1}^{m}p_{i}^{c_{i}'}\) ,其中 \(\sum\limits_{i=1}^{m}[c_{i}'<c_{i}] \ge 1,\sum\limits_{i=1}^{m}[c_{i} \le 2c_{i}']=m\) 。当 \(\sum\limits_{i=1}^{m}[c_{i}=1]=m\) 时,无解;否则构造 \(c_{i}'=\left\lceil \dfrac{c_{i}}{2} \right\rceil\) 即可,最终 \(n=\prod\limits_{i=1}^{m}p_{i}^{\left\lceil \frac{c_{i}}{2} \right\rceil}\) 即为所求。
点击查看代码
ll prime[100000],c[100000],cnt=0; void divide(ll n) { for(ll i=2;i<=sqrt(n);i++) { if(n%i==0) { cnt++; prime[cnt]=i; while(n%i==0) { c[cnt]++; n/=i; } } } if(n>1) { cnt++; prime[cnt]=n; c[cnt]++; } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); ll k,n=-1,sum=0,i; cin>>k; divide(k); for(i=1;i<=cnt;i++) { sum+=(c[i]==1); } if(k!=1&&sum!=cnt) { n=1; for(i=1;i<=cnt;i++) { n*=pow(prime[i],(ll)(ceil(1.0*c[i]/2))); } } cout<<n<<endl; fclose(stdin); fclose(stdout); return 0; }
\(T2\) 华二 \(0pts\)
-
部分分
- \(10pts\) :因有 \(1 \le a_{i} \le 3\) ,令 \(num_{1},num_{2},num_{3}\) 分别表示 \(1,2,3\) 出现的次数。然后就转化成了多重集的排列数,全排列个数为 \(\dfrac{n!}{num_{1}!num_{2}!num_{3}!}\) 。
-
正解
- 注意到 \(1 \le a_{i} \le 9\) ,考虑利用这个性质。
- 除了相等的数不能交换其余都是能够进行随便交换,交换两个相等的数等价于不交换,故一个数可以放到任何位置。
- \(1,5,7\) 与其他任何数都互质,故可以放到任何位置,最后再进行处理。
- \(2,3,4,6,8,9\) 中只有 \(6\) 与其他数都不互质,故两个 \(6\) 之间的数一定不能交换出去,等价于 \(6\) 的位置已经被固定了,设原来的数列就被 \(6\) 分成了 \(m\) 个区间。\(2,4,8\) 不能相互交换,相对位置(每两个数之间放其他的数的个数不固定)不变; \(3,9\) 不能相互交换,相对位置(每两个数之间放其他的数的个数不固定)不变。
- 设第 \(i\) 个区间内 \(2,4,8\) 出现的次数为 \(sum_{i,1}\) ,第 \(i\) 个区间内 \(3,9\) 出现的次数为 \(sum_{i,2}\) , \(num_{1},num_{2},num_{3}\) 分别表示 \(1,5,7\) 出现的次数。容斥后有 \(\dfrac{A_{num_{1}+num_{2}+num_{3}}^{num_{1}+num_{2}+num_{3}}}{A_{num_{1}}^{num_{1}}A_{num_{2}}^{num_{2}}A_{num_{3}}^{num_{3}}} \times \sum\limits_{i=1}^{m} \dbinom{sum_{i,1}+sum_{i,2}+1-1}{sum_{i,2}+1-1}\) 即为所求。
点击查看代码
ll a[100001],jc[100001],inv[100001],jc_inv[100001]; ll A(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc[n]*jc_inv[n-m]%p:0; } ll C(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?(jc[n]*jc_inv[m]%p)*jc_inv[n-m]%p:0; } int main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout); ll n,ans=1,sum1=0,sum2=0,sum3=0,sum4=0,sum5=0,i,p=998244353; cin>>n; inv[1]=1; jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1; for(i=2;i<=n;i++) { inv[i]=(p-p/i)*inv[p%i]%p; jc[i]=jc[i-1]*i%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } for(i=1;i<=n;i++) { cin>>a[i]; sum1+=(a[i]==2||a[i]==4||a[i]==8); sum2+=(a[i]==3||a[i]==9); sum3+=(a[i]==1); sum4+=(a[i]==5); sum5+=(a[i]==7); if(a[i]==6||i==n) { ans=ans*C(sum1+sum2+1-1,sum2+1-1,p)%p; sum1=sum2=0; } } ans=ans*(((A(n,sum3+sum4+sum5,p)*jc_inv[sum3]%p)*jc_inv[sum4]%p)*jc_inv[sum5]%p)%p; cout<<ans<<endl; fclose(stdin); fclose(stdout); return 0; }
\(T3\) 高爸 \(0pts\)
- 一个比较“显然”的结论:将最终的力量值设成原有的一条龙的力量值一定最优。
- 不会证明,咕了。
- 正解
-
三分
-
由 \(1 \le n \le 10^{5},1 \le x_{i} \le 10^9\) ,需要离散化。
-
观察到,类似带权中位数,发现 \(mp\) 通过一定的方案可以到达最小值,故为一个单谷函数,考虑三分。
-
设我们当前枚举到第 \(i\) 个位置,使 \(1 \sim i\) 条龙的力量值为 \(xx\) ,有 \(\sum\limits_{j=1}^{i}[x_{j}>xx]=p,\sum\limits_{j=1}^{i}[x_{j} \le xx]=i-p=q\) 。此时消耗的 \(mp\) 点数为 \(\begin{aligned}\sum\limits_{j=1}^{i}[x_{j}>xx] \times b(x_{j}-xx)+\sum\limits_{j=1}^{i}[x_{j} \le xx] \times a(xx-x_{j}) \\ =b(-p \times xx+\sum\limits_{j=1}^{i}[x_{j}>xx]x_{j})+a(q \times xx-\sum\limits_{j=1}^{i}[x_{j} \le xx]x_{j})\end{aligned}\) 。
-
类似 [ABC351F] Double Sum ,用权值树状数组维护 \(q,\sum\limits_{j=1}^{i}[x_{j} \le xx]x_{j}\) 得到 \(p=i-q,\sum\limits_{j=1}^{i}[x_{j}>xx]x_{j}=\sum\limits_{j=1}^{i}x_{j}-\sum\limits_{j=1}^{i}[x_{j} \le xx]x_{j}\) ,然后进行计算即可。
点击查看代码
ll x[100001],xx[100001],sum[100001],c[100001][2]; ll query(ll a[],ll x) { return lower_bound(a+1,a+1+a[0],x)-a; } ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll key,ll pd) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i][pd]+=key; } } ll getsum(ll x,ll pd) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i][pd]; } return ans; } ll f(ll mid,ll a,ll b,ll i) { return (sum[i]-getsum(mid,1)-(i-getsum(mid,0))*xx[mid])*b+(getsum(mid,0)*xx[mid]-getsum(mid,1))*a; } bool check(ll mid1,ll mid2,ll a,ll b,ll i) { return f(mid1,a,b,i)>f(mid2,a,b,i); } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); ll n,a,b,l,r,mid1,mid2,i; cin>>n>>a>>b; for(i=1;i<=n;i++) { cin>>x[i]; sum[i]=sum[i-1]+x[i]; xx[i]=x[i]; } sort(xx+1,xx+1+n); xx[0]=unique(xx+1,xx+1+n)-(xx+1); add(xx[0],query(xx,x[1]),1,0); add(xx[0],query(xx,x[1]),x[1],1); cout<<"0"<<endl; for(i=2;i<=n;i++) { add(xx[0],query(xx,x[i]),1,0); add(xx[0],query(xx,x[i]),x[i],1); l=1; r=xx[0]; while(l<=r) { mid1=l+(r-l)/3; mid2=r-(r-l)/3; if(check(mid1,mid2,a,b,i)==true) { l=mid1+1; } else { r=mid2-1; } } cout<<f(l,a,b,i)<<endl; } fclose(stdin); fclose(stdout); return 0; }
-
-
带权中位数
- 把权值 \(a,b\) 拆成分别有 \(a,b\) 个点。然后就转化为了 AcWing 104. 货仓选址 。
-
\(T4\) 金牌 \(0pts\)
-
部分分
- \(69/99pts\)
-
倍增求 \(LCA\) ,倍增优化跳父亲。
- 把通过 \(x\) 和 \(y\) 的路径拆成三部分,分别是从 \(l(l \in Subtree(x))\) 到 \(x\) ,从 \(x\) 到 \(y\) ,从 \(y\) 到 \(r(r \in Subtree(y))\) 。
- 钦定 \(1\) 为根节点。
- 第一遍 \(DFS\) 时,设 \(f_{x}\) 表示以 \(x\) 为根的子树内的点到 \(i\) 的长度的价值之和,即 \(f_{x}=\sum\limits_{y \in Son(x)}(2f_{y}+2)\) 。
- 第二遍 \(DFS\) 时,进行换根。设 \(g_{x}\) 表示除原以 \(x\) 为根的子树内的点外的点到 \(x\) 的长度的价值之和。仍钦定 \(1\) 为根节点,状态转移方程为 \(g_{x}=\begin{cases}0 & x=1 \\ 2g_{fa_{x}}+2+2(f_{fa_{x}}-2f_{x}-2) & x \ne 1\end{cases}\) 。
- 然后进行分类讨论,求出 \(x\) 和 \(y\) 的 \(LCA\) ,记为 \(rt\) 。当 \(x \ne rt,y \ne rt\) 时,从乘法原理的角度分析,有 \((f_{x}+1)(f_{y}+1)2^{dis_{x,y}}\) 即为所求;当 \(x,y\) 中有一个等于 \(rt\) 时,钦定 \(x=rt\) ,记 \(son(son \in Son(x))\) 满足 \(y\) 在以 \(son\) 为根的子树内,从乘法原理的角度分析,有 \(g_{son}(f_{y}+1)2^{dis_{son,y}}\) 即为所求。
- 略带卡常。
点击查看代码
#define LOCAL namespace IO { #ifdef LOCAL FILE *Fin(fopen("d.in","r")),*Fout(fopen("d.out","w")); #else FILE *Fin(stdin),*Fout(stdout); #endif class qistream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];int p;public:qistream(FILE *_fp=stdin ):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;} qistream &operator>>(char &str){str = getch();while(isspace(str))str = getch();return*this;} template<class T>qistream &operator>>(T &x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=(x<<3)+(x<<1)+(buf[p]^48);x=flag?-x:x;return*this;}char getch(){return buf[p++];}qistream &operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i;for(i=0;ch>' '; ++i,ch=getch())str[i] = ch;str[i] = '\0';return*this;}}qcin(Fin); class qostream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];int p;public:qostream(FILE *_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream &operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=~x+1,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while (x);for(int i=0,j=len-1;i<j;++i,--j)swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream &operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}} qcout(Fout);}using namespace IO; #define cin qcin #define cout qcout const ll p=998244353; struct node { int nxt,to; }e[2000001]; int head[2000001],fa[2000001][25],N,cnt=0; ll dep[2000001],jc[2000001],f[2000001],g[2000001]; static inline void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int father) { fa[x][0]=father; dep[x]=dep[father]+1; f[x]=0; for(register int i=1;(1<<i)<=dep[x];++i) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(register int i=head[x];i;i=e[i].nxt) { if(e[i].to^father) { dfs(e[i].to,x); f[x]=(f[x]+((f[e[i].to]<<1)%p+2)%p)%p; } } } void reroot(int x,int father) { for(register int i=head[x];i;i=e[i].nxt) { if(e[i].to^father) { g[e[i].to]=(((g[x]<<1)%p+2)%p+(((f[x]-2*f[e[i].to]%p-2+p)%p)<<1)%p)%p; reroot(e[i].to,x); } } } static inline int lca(int x,int y) { if(dep[x]>dep[y]) { swap(x,y); } for(register int i=N;i>=0;--i) { if(dep[x]+(1<<i)<=dep[y]) { y=fa[y][i]; } } if(!(x^y)) { return x; } else { for(register int i=N;i>=0;--i) { if(fa[x][i]^fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } } int main() { freopen("d.in","r",stdin); freopen("d.out","w",stdout); register int n,m,u,v,rt,son,i,j; cin>>n; N=log2(n)+1; jc[0]=1; for(i=1;i<n;++i) { cin>>u>>v; add(u,v); add(v,u); jc[i]=(jc[i-1]<<1)%p; } jc[n]=(jc[n-1]<<1)%p; dfs(1,0); reroot(1,0); cin>>m; for(i=1;i<=m;++i) { cin>>u>>v; rt=lca(u,v); if((!(rt^u))||(!(rt^v))) { if(!(rt^v)) { swap(u,v); } son=v; for(j=N;j>=0;--j) { if(dep[fa[son][j]]>dep[u]) { son=fa[son][j]; } } cout<<(jc[dep[v]-dep[son]]*g[son]%p)*((f[v]+1)%p)%p<<endl; } else { cout<<(((f[u]+1)%p)*((f[v]+1)%p)%p)*jc[dep[u]+dep[v]-(dep[rt]<<1)]%p<<endl; } } fclose(stdin); fclose(stdout); return 0; }
-
\(Tarjan\) 求 \(LCA\) ,倍增优化跳父亲。
- 略带卡常。
点击查看代码
#define LOCAL namespace IO { #ifdef LOCAL FILE *Fin(fopen("d.in","r")),*Fout(fopen("d.out","w")); #else FILE *Fin(stdin),*Fout(stdout); #endif class qistream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];int p;public:qistream(FILE *_fp=stdin ):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;} qistream &operator>>(char &str){str = getch();while(isspace(str))str = getch();return*this;} template<class T>qistream &operator>>(T &x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=(x<<3)+(x<<1)+(buf[p]^48);x=flag?-x:x;return*this;}char getch(){return buf[p++];}qistream &operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i;for(i=0;ch>' '; ++i,ch=getch())str[i] = ch;str[i] = '\0';return*this;}}qcin(Fin); class qostream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];int p;public:qostream(FILE *_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream &operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=~x+1,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while (x);for(int i=0,j=len-1;i<j;++i,--j)swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream &operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}} qcout(Fout);}using namespace IO; #define cin qcin #define cout qcout const ll p=998244353; struct node { int nxt,to; }e[2000001]; int head[2000001],tarjan_f[2000001],u[2000001],v[2000001],vis[2000001],rt[2000001],dep[2000001],fa[2000001][25],cnt=0; ll jc[2000001],f[2000001],g[2000001]; vector<pair<int,int> >ask[2000001]; inline void add(rint u,rint v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(rint x,rint father) { fa[x][0]=father; dep[x]=dep[father]+1; f[x]=0; for(rint i=1;(1<<i)<=dep[x];++i) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^father) { dfs(e[i].to,x); f[x]=(f[x]+((f[e[i].to]<<1)%p+2)%p)%p; } } } void reroot(rint x,rint father) { for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^father) { g[e[i].to]=(((g[x]<<1)%p+2)%p+(((f[x]-2*f[e[i].to]%p-2+p)%p)<<1)%p)%p; reroot(e[i].to,x); } } } int find(rint x) { return ((tarjan_f[x]==x)?tarjan_f[x]:tarjan_f[x]=find(tarjan_f[x])); } void tarjan(rint x) { vis[x]=1; for(rint i=head[x];i;i=e[i].nxt) { if(vis[e[i].to]==0) { tarjan(e[i].to); tarjan_f[e[i].to]=x; } } for(rint i=0;i<ask[x].size();++i) { if(vis[ask[x][i].first]==2) { rt[ask[x][i].second]=find(ask[x][i].first); } } vis[x]=2; } int main() { freopen("d.in","r",stdin); freopen("d.out","w",stdout); rint n,m,uu,vv,son,i,j,N; cin>>n; N=log2(n)+1; jc[0]=1; for(i=1;i<n;++i) { cin>>uu>>vv; add(uu,vv); add(vv,uu); jc[i]=(jc[i-1]<<1)%p; tarjan_f[i]=i; } jc[n]=(jc[n-1]<<1)%p; tarjan_f[n]=n; dfs(1,0); reroot(1,0); cin>>m; for(i=1;i<=m;++i) { cin>>u[i]>>v[i]; if(u[i]==v[i]) { rt[i]=u[i]; } else { ask[u[i]].push_back(make_pair(v[i],i)); ask[v[i]].push_back(make_pair(u[i],i)); } } tarjan(1); for(i=1;i<=m;++i) { if((!(rt[i]^u[i]))||(!(rt[i]^v[i]))) { if(!(rt[i]^v[i])) { swap(u[i],v[i]); } son=v[i]; for(j=N;j>=0;--j) { if(dep[fa[son][j]]>dep[u[i]]) { son=fa[son][j]; } } cout<<(jc[dep[v[i]]-dep[son]]*g[son]%p)*((f[v[i]]+1)%p)%p<<endl; } else { cout<<(((f[u[i]]+1)%p)*((f[v[i]]+1)%p)%p)*jc[dep[u[i]]+dep[v[i]]-(dep[rt[i]]<<1)]%p<<endl; } } fclose(stdin); fclose(stdout); return 0; }
-
- \(100pts\) :树剖求 \(LCA\) 和 \(son\)。
-
略带卡常。
点击查看代码
#define LOCAL namespace IO { #ifdef LOCAL FILE *Fin(fopen("d.in","r")),*Fout(fopen("d.out","w")); #else FILE *Fin(stdin),*Fout(stdout); #endif class qistream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];ll p;public:qistream(FILE *_fp=stdin ):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;} qistream &operator>>(char &str){str = getch();while(isspace(str))str = getch();return*this;} template<class T>qistream &operator>>(T &x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=(x<<3)+(x<<1)+(buf[p]^48);x=flag?-x:x;return*this;}char getch(){return buf[p++];}qistream &operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();ll i;for(i=0;ch>' '; ++i,ch=getch())str[i] = ch;str[i] = '\0';return*this;}}qcin(Fin); class qostream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];ll p;public:qostream(FILE *_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream &operator<<(T x){ll len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=~x+1,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while (x);for(ll i=0,j=len-1;i<j;++i,--j)swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream &operator<<(char*str){for(ll i=0;str[i];++i)putch(str[i]);return*this;}} qcout(Fout);}using namespace IO; #define cin qcin #define cout qcout const ll p=998244353; struct node { int nxt,to; }e[2000010]; int head[2000010],siz[2000010],fa[2000010],dep[2000010],son[2000010],top[2000010],cnt=0; ll jc[2000010],f[2000010],g[2000010]; inline void add(rint u,rint v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(rint x,rint father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^father) { dfs(e[i].to,x); f[x]=(f[x]+((f[e[i].to]<<1)%p+2)%p)%p; siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void reroot(rint x,rint father,rint id) { top[x]=id; if(son[x]) { g[son[x]]=(((g[x]<<1)%p+2)%p+(((f[x]-2*f[son[x]]%p-2+p)%p)<<1)%p)%p; reroot(son[x],x,id); for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^father&&e[i].to^son[x]) { g[e[i].to]=(((g[x]<<1)%p+2)%p+(((f[x]-2*f[e[i].to]%p-2+p)%p)<<1)%p)%p; reroot(e[i].to,x,e[i].to); } } } } inline int lca(rint u,rint v) { while(top[u]^top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } inline int dirson(rint u,rint v) { if(dep[u]>dep[v]) { swap(u,v); } while(top[u]^top[v]) { if(dep[top[u]]>dep[top[v]]) { if(!(fa[top[u]]^v)) { return top[u]; } u=fa[top[u]]; } else { if(!(fa[top[v]]^u)) { return top[v]; } v=fa[top[v]]; } } return (dep[u]<dep[v])?son[u]:son[v]; } int main() { rint n,m,u,v,rt,sonn,i; cin>>n; jc[0]=1; for(i=1;i<n;++i) { cin>>u>>v; add(u,v); add(v,u); jc[i]=(jc[i-1]<<1)%p; } jc[n]=(jc[n-1]<<1)%p; dfs(1,0); reroot(1,0,1); cin>>m; for(i=1;i<=m;++i) { cin>>u>>v; rt=lca(u,v); if((!(rt^u))||(!(rt^v))) { if(!(rt^v)) { swap(u,v); } sonn=dirson(u,v); cout<<(jc[dep[v]-dep[sonn]]*g[sonn]%p)*((f[v]+1)%p)%p<<endl; } else { cout<<(((f[u]+1)%p)*((f[v]+1)%p)%p)*jc[dep[u]+dep[v]-(dep[rt]<<1)]%p<<endl; } } return 0; }
-
- \(69/99pts\)
-
正解
- 找 \(son\) 的过程等价于找 \(y\) 的 \(dep_{y}-dep_{x}-1\) 级祖先,可用长链剖分维护 luogu P5903 【模板】树上 K 级祖先 。
- 考虑在 \(DFS\) 的过程中维护一个栈,记录当前节点的所有祖先。可将询问离线下来放到节点上,当遍历到该节点时,它的 \(k\) 级祖先一定在当前栈内向下 \(k\) 个。
- 略带卡常,多交几发就过了。
点击查看代码
#define LOCAL namespace IO { #ifdef LOCAL FILE *Fin(fopen("d.in","r")),*Fout(fopen("d.out","w")); #else FILE *Fin(stdin),*Fout(stdout); #endif class qistream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];ll p;public:qistream(FILE *_fp=stdin ):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;} qistream &operator>>(char &str){str = getch();while(isspace(str))str = getch();return*this;} template<class T>qistream &operator>>(T &x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=(x<<3)+(x<<1)+(buf[p]^48);x=flag?-x:x;return*this;}char getch(){return buf[p++];}qistream &operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();ll i;for(i=0;ch>' '; ++i,ch=getch())str[i] = ch;str[i] = '\0';return*this;}}qcin(Fin); class qostream {static const size_t SIZE=1<<16,BLOCK=32;FILE*fp;char buf[SIZE];ll p;public:qostream(FILE *_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream &operator<<(T x){ll len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=~x+1,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while (x);for(ll i=0,j=len-1;i<j;++i,--j)swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream &operator<<(char*str){for(ll i=0;str[i];++i)putch(str[i]);return*this;}} qcout(Fout);}using namespace IO; #define cin qcin #define cout qcout const ll p=998244353; struct node { int nxt,to; }e[2000010]; int head[2000010],tarjan_f[2000010],u[2000010],v[2000010],k[2000010],kfa[2000010],vis[2000010],rt[2000010],dep[2000010],s[2000010],cnt=0,top=0; ll jc[2000010],f[2000010],g[2000010]; vector<pair<int,int> >ask_lca[2000010]; vector<int>ask_kfa[2000010]; inline void add(rint u,rint v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(rint x,rint fa) { dep[x]=dep[fa]+1; for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^fa) { dfs(e[i].to,x); f[x]=(f[x]+((f[e[i].to]<<1)%p+2)%p)%p; } } } void reroot(rint x,rint fa) { top++; s[top]=x; for(rint i=0;i<ask_kfa[x].size();i++) { kfa[ask_kfa[x][i]]=s[top-k[ask_kfa[x][i]]]; } for(rint i=head[x];i;i=e[i].nxt) { if(e[i].to^fa) { g[e[i].to]=(((g[x]<<1)%p+2)%p+(((f[x]-2*f[e[i].to]%p-2+p)%p)<<1)%p)%p; reroot(e[i].to,x); } } top--; } int find(rint x) { return ((tarjan_f[x]==x)?tarjan_f[x]:tarjan_f[x]=find(tarjan_f[x])); } void tarjan(rint x) { vis[x]=1; for(rint i=head[x];i;i=e[i].nxt) { if(vis[e[i].to]==0) { tarjan(e[i].to); tarjan_f[e[i].to]=x; } } for(rint i=0;i<ask_lca[x].size();++i) { if(vis[ask_lca[x][i].first]==2) { rt[ask_lca[x][i].second]=find(ask_lca[x][i].first); } } vis[x]=2; } int main() { rint n,m,uu,vv,i,j; cin>>n; jc[0]=1; for(i=1;i<n;++i) { cin>>uu>>vv; add(uu,vv); add(vv,uu); jc[i]=(jc[i-1]<<1)%p; tarjan_f[i]=i; } jc[n]=(jc[n-1]<<1)%p; tarjan_f[n]=n; dfs(1,0); cin>>m; for(i=1;i<=m;++i) { cin>>u[i]>>v[i]; if(u[i]==v[i]) { rt[i]=u[i]; } else { ask_lca[u[i]].push_back(make_pair(v[i],i)); ask_lca[v[i]].push_back(make_pair(u[i],i)); } } tarjan(1); for(i=1;i<=m;++i) { if((!(rt[i]^u[i]))||(!(rt[i]^v[i]))) { if(!(rt[i]^v[i])) { swap(u[i],v[i]); } k[i]=dep[v[i]]-dep[u[i]]-1; ask_kfa[v[i]].push_back(i); } } reroot(1,0); for(i=1;i<=m;++i) { if((!(rt[i]^u[i]))||(!(rt[i]^v[i]))) { cout<<(jc[dep[v[i]]-dep[kfa[i]]]*g[kfa[i]]%p)*((f[v[i]]+1)%p)%p<<endl; } else { cout<<(((f[u[i]]+1)%p)*((f[v[i]]+1)%p)%p)*jc[dep[u[i]]+dep[v[i]]-(dep[rt[i]]<<1)]%p<<endl; } } return 0; }
总结
- 感觉这场比赛不是自己能力所能接受的,故写完 \(T1\) 正解和 \(T2,T3\) 骗分,打到 \(8:30\) 就不打了。
- 要学会从身边各种事物中找到做题的灵感。
- 菜就多练。
后记
- 有大样例,好耶。
- @User-Unauthorized :这是我们考过最简单的一套题。
- @User-Unauthorized 给 \(T4\) 各造了一个 \(1pts\) 的 \(hack\) 数据点。
- \(T4\) 赛时时限 \(1s\) ,赛后改成了 \(1.5s\) 。
- @wkh2008 想挑战用倍增求 \(LCA\) 挑战 @User-Unauthorized 的数据,但他失败了。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18003611,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。