「考试总结」2022-02-06
A.我醉
分奇数和偶数二分回文串长度,判断是不是存在使用点分治
现在问题是判断是不是存在一个跨过当前分治重心的长度为 的回文串,可以先扫出来每个联通块中心到根正向/反向的哈希值
再扫一次联通块里面的每个点,求出来剩下需求的长度 中是不是有和自己根链上相同哈希值的串,再判断自己根链上剩下的一部分是不是回文串即可
直觉上是 个 ,不太能跑过,所以乱搞做法就是随机一个排列,逐个枚举点作为回文串中心,暴力判断是不是可以扩展
Code Display
const int N=2e5+10;
int tim,mark[N],n,id[N];
vector<pair<int,int> > G[N];
unordered_map<ull,int>mp,dou;
inline int solve(int cen){
mark[cen]=++tim;
vector<tuple<ull,int,int> > now,pre;
for(auto e:G[cen]) now.emplace_back((ull)e.sec,e.fir,e.fir),mark[e.fir]=tim;
for(int len=0;len<2*n;++len){
mp.clear(); dou.clear();
swap(now,pre); now.clear();
for(auto tmp:pre){
int top,nowt;
ull hs;
tie(hs,top,nowt)=tmp;
if(!mp.count(hs)) mp[hs]=top;
else if(mp[hs]!=top) dou[hs]=1;
}
for(auto tmp:pre){
int top,nowt;
ull hs;
tie(hs,top,nowt)=tmp;
if(dou[hs]) now.emplace_back(tmp);
}
if(!now.size()) return len;
swap(now,pre); now.clear();
for(auto tmp:pre){
int top,nowt;
ull hs;
tie(hs,top,nowt)=tmp;
for(auto e:G[nowt]){
int t=e.fir; if(mark[t]==tim) continue;
mark[t]=tim;
now.emplace_back(hs*133331+(ull)e.sec,top,t);
}
}
} return 2*n;
}
mt19937 Rand(260823);
signed main(){
freopen("name.in","r",stdin); freopen("name.out","w",stdout);
n=read();
for(int i=1;i<n;++i){
int x=read(),y=read(),z=read();
G[x].push_back({i+n,z});
G[y].push_back({i+n,z});
G[i+n].push_back({x,z});
G[i+n].push_back({y,z});
}
if(G[1].size()==n-1){
bool vis[1001]={};
for(auto t:G[1]){
if(vis[t.sec]) print(2),exit(0);
else vis[t.sec]=1;
}
print(1); exit(0);
}
for(int i=1;i<2*n;++i) id[i]=i; random_shuffle(id+1,id+(n<<1));
int ans=0;
for(int i=1;i<2*n;++i){
ckmax(ans,solve(id[i]));
if(clock()/CLOCKS_PER_SEC>=5.97) break;
}
print(ans);
return 0;
}
B.梧桐依旧
将所有满秩的 阶矩阵视作一个群 ,那么原问题就是求解在群 作用下的不动点个数
根据 引理:
也就是说等价类个数等于不动点个数的平均值,那么不动点总数就是等价类个数乘群的大小
第一个部分就是求满秩矩阵的个数,本质上就是选出来 个线性无关的向量的方案
选择第一个向量时方案是 ,因为这个前面的向量所形成的维度是 ,所以这个向量可以向任意方向扩展(也就是说 即可),同时不能是 向量
在选择第二个向量的时候不能和第一个向量线性相关,也就是 ,而 的选值是 ,所以方案数是
那么第三个向量的选值方案数和上面类似,这里 有 个,所以不合法方案是 ,选向量的方案数是
依次类推,群的大小就是
剩下要求的就是等价类个数,这里有一个结论是左乘矩阵等价于做初等行变换,即 左乘 的含义就是将矩阵 的第 行乘 加到第 行上( 时附加系数是 )
那么这里所有形式的初等行变换就可以视作一个置换群
不难发现秩不同的矩阵一定不在一个等价类 中,那么枚举矩阵的秩 逐个计算
考虑对于一个特定的秩 ,矩阵的个数的计算是和群大小计算类似的 ,只考虑线性无关的这些行就行了
接下来计算 的行变换 种数,这个和数一个 阶满秩矩阵是等价的,所以方案数是
考虑一个可重集合 ,其中元素满足 形式,不难发现这就是特定秩的矩阵总数,那么发现其每个 中的元素都在 出现了 次,这是因为全集是每个等价类是通过不同行变换得到的
不要漏记了全 矩阵作为一个等价类!
综上所述:答案为
发现分子是一段 的后缀乘积,分母是前缀乘积,所以先算后缀积,再维护前缀积的逆元就可以 做了
Code Display
const int N=3e7+10;
int pre[N],suf[N],pw[N],n,p;
signed main(){
freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
n=read(); p=read();
pw[0]=1; rep(i,1,n) pw[i]=mul(pw[i-1],p);
int siz_G=1;
for(int i=0;i<n;++i) ckmul(siz_G,del(pw[n],pw[i]));
int sum=1;
pre[0]=suf[n+1]=1;
for(int i=n;i>=1;--i) suf[i]=mul(suf[i+1],del(pw[i],1));
pre[n]=ksm(suf[1],mod-2);
Down(i,n,2) pre[i-1]=mul(pre[i],del(pw[i],1));
for(int i=1;i<=n;++i) ckadd(sum,mul(suf[n-i+1],pre[i]));
print(mul(siz_G,sum));
return 0;
}
C.卿且去
将 定义为偏序集,这里可比就是 ,反之不可比
那么原题中 选出来一组质数之后 就是要求得到集合的最长反链,使用 Dilworth定理 就可以知道是要求最小的链划分
这时候可以贪心得到选数字策略:每次选出来当前集合里面最小的数 ,并取 作为一组新的链划分
按照上述策略证明这些数字都没被覆盖过是容易的,同时如果在选择 之后不选择 那么 又要新开一组链来覆盖之,所以该策略一定能得到最小链划分也是正确的
问题至此变成了按上述策略拆集合后链划分的总数量,将每个划分的 的贡献挂到链首元素处,再考虑每个整数 的贡献:
简记 为 的奇数个数, 表示 的质因子个数
-
那么只要选出其因子的一个就会产生贡献,数值是
-
是偶数但是不是 的倍数时,如果 的质因子中有且只有 在挑出来的质数集合中那么贡献,否则不贡献,这等价于钦定了 的每个质因子出现在 "挑出来的质数集合" 与否,而对其它因子是否出现并不关注,所以方案数为
问题转化成了求奇数的 的前缀和, 显然是积性函数,使用 筛即可
Code Display
const int N=6e6+10;
int n,pin=-1;
namespace Min_25{
int pri[N/10],g[N],cnt,Basnum,id1[N],id2[N],tot,R[N],block;
bool fl[N];
inline int getid(int x){return x<=block?id1[x]:id2[n/x];}
inline int S(int n,int i){
if(pri[i]>=n) return 0;
int sum=del(g[getid(n)],mul(Basnum,i-1));
for(int j=i+1;j<=cnt&&pri[j]*pri[j]<=n;++j){
int prd=pri[j],Enum=1;
while(n>=prd){
ckadd(sum,mul(Basnum,add(Enum!=1,S(n/prd,j))));
Enum++;
prd*=pri[j];
}
}
return sum;
}
inline int solve(int NN,int basnum){
tot=cnt=0;
memset(pri,0,sizeof(pri));
memset(g,0,sizeof(g));
memset(id1,0,sizeof(id1));
memset(id2,0,sizeof(id2));
memset(R,0,sizeof(R));
memset(fl,0,sizeof(fl));
n=NN; Basnum=basnum; block=sqrt(n);
for(int i=2;i<=block;++i){
if(!fl[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<=block;++j){
fl[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
for(int l=1,r;l<=n;l=r+1){
R[++tot]=r=n/(n/l);
g[tot]=((R[tot]+1)/2-1)%mod;
if(R[tot]<=block) id1[R[tot]]=tot;
else id2[n/R[tot]]=tot;
}
for(int j=2;j<=cnt;++j){
for(int i=tot;pri[j]*pri[j]<=R[i];--i){
int pos=getid(R[i]/pri[j]);
ckdel(g[i],del(g[pos],j-2));
}
}
if(pin==-1) pin=add(g[getid(n)],1);
rep(i,1,tot) ckmul(g[i],basnum);
return S(NN,1);
}
}
using Min_25::solve;
signed main(){
freopen("yyds.in","r",stdin); freopen("yyds.out","w",stdout);
n=read();
int tmp=solve(n,(mod+1)/2),val=ksm(2,pin);
int ans1=mul(val,del(((n+1)/2-1)%mod,tmp));
ckmul(val,(mod+1)>>1);
int ans2=mul(val,add(1,solve(n/2,(mod+1)/2)));
print(add(ans1,ans2));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律