【考试总结】2022-07-05
数叶子
点之间互相独立,对答案的贡献之和它的度数有关,设当前计算的点的度数为 \(d\)
此时问题本质上是在 \([1,m]\) 中放 \(d\) 块板,每种放法向答案贡献划分出来的 \(d+1\) 个区间两两相邻的长度的乘积
使用 \(x=\sum\limits_{i=1}^x[1]\) 的想法处理乘积,也就是让每相邻的一对计算长度乘积次,通过枚举 只包含一个点的区间的 可以达到这个效果
此时得到答案表达式为
\[\sum_{i=1}^n\binom{m-deg_i}{n-1-deg_i}(n-deg_i-1)!deg_i!\sum_{j=1}^m\binom{m-j}{deg_i-1}j(m-j+1) \]用吸收恒等式处理 \(m-j+1\) 再直接枚举组合数上侧,再将 \(m-j+1\) 拆成 \(m+2-(j+1)\) 分别求和,一侧是直接上指标求和,另一个再用一遍吸收恒等式就能直接求和了
组合数处理很有技巧
Code Display
const int N=1e6+10;
int n,m,deg[N],fac[N],ifac[N];
int dfac1[N];
inline int binom(int n,int k){return mul(fac[n],mul(ifac[k],ifac[n-k]));}
inline int calc(int d){
int res=mul(fac[d],fac[n-d-1]*d%mod);
return mul(mul(dfac1[d+1],ifac[n-1-d]),res);
}
signed main(){
freopen("leaf.in","r",stdin);
freopen("leaf.out","w",stdout);
n=1e6+1; fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i);
n=read(); m=read();
for(int i=1;i=1;--i) dfac1[i]=mul(dfac1[i+1],m-i+1);
int d=1,v=1;
for(int i=1;i<=n-1-d;++i) ckmul(v,m-d-i+1);
int ans=0;
for(int i=1;i<=n;++i){
int coef=calc(deg[i]);
auto Dfac=[&](int n,int m){
int res=1;
for(int i=1;i<=m;++i) ckmul(res,n-i+1);
return res;
};
int sum=(m+2)*Dfac(m+1,deg[i]+1)%mod*ifac[deg[i]+1]%mod-(deg[i]+1)*ifac[deg[i]+2]%mod*Dfac(m+2,deg[i]+2);
sum=(sum%mod+mod)%mod;
ans+=sum*coef%mod;
}
print(ans%mod);
return 0;
}
多边形
二分答案 \(\rm R\) ,在平面直角坐标系上画出一个半径为 \(R\) 的圆,此时目标多边形的每条边都和该圆相切
求出来给定的 \(n\) 个点向这个圆的切线,称两个切点形成的劣弧为这个点在圆上对应的区间。那么多边形某条边和圆的切点在某个给定点的对应区间中等价于这个点在多边形外
问题变成了使用 \(m\) 个点覆盖圆上的 \(n\) 个已知区间,贪心得到 \(m\) 个点中的每一个都会在区间的端点上。
暴力做法就是枚举所有 \(n\) 个区间的 \(2n\) 个端点作为原点,展开成链之后在原点放一个,在没被覆盖的区间中右端点最小者在右端点放一个点。
这个过程可以先倍增优化,断环成链之后双指针扫出来每个区间的后继
不难发现根据这个做法,得到的多边形不一定闭合,所以这题是错题吗?水博的 dalao 们还请指教
Code Display
const int N=2e5+10;
const double pi=acos(-1);
int n,m,bz[20][N];
struct point{double x,y,angle,dist;}p[N];
struct interval{double l,r;}inter[N];
inline bool check(double R){
for(int i=1;i<=n;++i){
double tmp=acos(R/p[i].dist);
inter[i].l=p[i].angle-tmp;
inter[i].r=p[i].angle+tmp;
if(inter[i].l<0) inter[i].l+=2*pi;
if(inter[i].r<0) inter[i].r+=2*pi;
if(inter[i].r=0;--j) if(m>>j&1) x=bz[j][x];
if(!x||x>=n+i) return 1;
}
return 0;
}
int main(){
freopen("polygon.in","r",stdin);
freopen("polygon.out","w",stdout);
n=read(); m=read();
double l=0,r=1e10,ans=l;
for(int i=1;i<=n;++i){
p[i].x=read(),p[i].y=read();
p[i].angle=atan2(p[i].y,p[i].x);
p[i].dist=sqrt(p[i].x*p[i].x+p[i].y*p[i].y);
ckmin(r,p[i].dist);
}
if(m>=n) printf("%.10lf\n",r),exit(0);
while(r-l>1e-9){
double mid=(l+r)/2;
if(check(mid)) ans=mid,l=mid;
else r=mid;
}
printf("%.10lf\n",ans);
return 0;
}
树论
原题 Surprise me,本题将 \(d(x,y)^k\) 变成 \(\displaystyle\sum_{i=0}^k\binom ki (d_x-d_{\rm LCA})^i(d_y-d_{\rm LCA})^{k-i}\) 进行计算即可,那么需要记录虚树上边权
在从儿子转移到父亲的过程本质上是用 \((w+w')^k\) 代替 \(w^k\) 的过程,在树形 \(\rm DP\) 的过程中用二项式定理展开维护即可
时间复杂度 \(\Theta(n\log n(\log n+k^2))\)
Code Display
const int mod=998244353,N=1e5+10;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int del(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return x*y-x*y/mod*mod;}
inline void ckadd(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
inline void ckdel(int &x,int y){x=x-y<0?x-y+mod:x-y;}
inline void ckmul(int &x,int y){x=x*y-x*y/mod*mod;}
inline int ksm(int x,int y){int res=1; for(;y;y>>=1,ckmul(x,x)) if(y&1) ckmul(res,x); return res;}
int n,K,ans[20];
int inv[N],ifac[N],fac[N],pw[N][20];
int lg[N<<1],st[20][N<<1],inpos[N],scnt;
int tim,dfn[N],dep[N];
vector G[N];
int phi[N],pri[N],mu[N],pcnt;
bool isc[N];
inline void dfs(int x,int fat){
dep[x]=dep[fat]+1;
st[0][inpos[x]=++scnt]=x;
dfn[x]=++tim;
for(auto t:G[x]) if(t^fat) dfs(t,x),st[0][++scnt]=x;
return ;
}
inline int get_Lca(int x,int y){
if(x==y) return x;
x=inpos[x],y=inpos[y]; if(x>y) swap(x,y); int t=lg[y-x+1];
int node1=st[t][x],node2=st[t][y-(1<dep[node2]) return node2;
return node1;
}
int stk[N],top;
vector virt[N];
int nowd,dp[N][20],coef[N];
inline void insert(int x){
if(top<=1){stk[++top]=x; return ;}
int tl=get_Lca(x,stk[top]);
while(top>1&&dfn[stk[top-1]]>=dfn[tl]) virt[stk[top-1]].emplace_back(stk[top]),--top;
if(stk[top]^tl) virt[tl].emplace_back(stk[top]),stk[top]=tl;
return stk[++top]=x,void();
}
inline void DP(int x){
if(x%nowd==0) dp[x][0]=phi[x];
for(auto t:virt[x]){
DP(t);
for(int i=K;i>=0;--i) if(dp[t][i]){
for(int j=1;j+i<=K;++j){
int coef=mul(ifac[j],pw[dep[t]-dep[x]][j]);
(dp[t][i+j]+=coef*dp[t][i])%=mod;
}
}
for(int i=0;i<=K;++i) if(dp[x][i]) for(int j=0;i+j<=K;++j){
ans[i+j]+=coef[nowd]*dp[x][i]%mod*dp[t][j]%mod;
}
for(int i=0;i<=K;++i) ckadd(dp[x][i],dp[t][i]);
}
return ;
}
inline void clear(int x){
for(auto t:virt[x]) clear(t);
memset(dp[x],0,sizeof(dp[x]));
virt[x].clear();
return ;
}
int node[N],cnt;
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=1e5; K=10;
inv[0]=fac[0]=pw[0][0]=1;
for(int i=1;i<=n;++i){
fac[i]=mul(fac[i-1],i);
pw[i][0]=1;
for(int j=1;j<=K;++j) pw[i][j]=mul(pw[i][j-1],i);
}
ifac[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;--i){
ifac[i-1]=mul(ifac[i],i);
inv[i]=mul(ifac[i],fac[i-1]);
}
for(int i=2;i<=n;++i){
if(!isc[i]){
pri[++pcnt]=i;
phi[i]=i-1;
mu[i]=-1;
}
for(int j=1;j<=pcnt&&i*pri[j]<=n;++j){
isc[i*pri[j]]=1;
if(i%pri[j]==0){
phi[i*pri[j]]=phi[i]*pri[j];
mu[i*pri[j]]=0;
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
mu[i*pri[j]]=-mu[i];
}
}
n=read(); K=read();
phi[1]=mu[1]=1;
for(int i=1;i>1]+1;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;j+=i){
coef[j]=(coef[j]+inv[phi[i]]*i*mu[j/i]%mod+mod)%mod;
}
}
for(int d=1;d<=n;++d){
cnt=0;
for(int j=d;j<=n;j+=d) node[++cnt]=j;
sort(node+1,node+cnt+1,[&](int x,int y){return dfn[x]