啊!终于改完题了!!!
说实话,考试的时候睡着了好几次,于是只有暴力分
很困的,没有办法思考...................................................................................
但是我一眼看出来第一题一定是个点分治,然后第二题只会打暴力
第三题蒙了个结论然后爆蛋了......
T1 我醉
这个其实二分是我的瓶颈,分奇数偶数二分,然后用点分治check就好了
对于每个分治中心来说,一个回文串一定是一条长的和一条短的组成,把短的的hash值放到hash表里
然后用大的查找就行了
AC_code
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define ull unsigned int
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=1e5+5;
const ull bas=133131;
ull ba[N],hs[N],zs[N];
inline ull get(int l,int r){return hs[r]-hs[l]*ba[r-l];}
int n,ans;
gp_hash_table<ull,int> mp;
struct E{int to,nxt,val;}e[N*2];
int head[N],rp;
void add_edg(int x,int y,int z){
e[++rp].to=y;e[rp].nxt=head[x];
e[rp].val=z;head[x]=rp;
}
int len,res;
bool vis[N];
int rt,mx,ms[N],siz[N];
void findrt(int x,int f,int sz){
siz[x]=1;ms[x]=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
findrt(y,x,sz);
siz[x]+=siz[y];
ms[x]=max(ms[x],siz[y]);
}
ms[x]=max(ms[x],sz-siz[x]);
if(ms[x]<mx)mx=ms[x],rt=x;
}
void dfs_fi(int x,int f,int d){
if(res)return ;
if(d<=(len>>1))mp[hs[d]]++;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
hs[d+1]=hs[d]*bas+e[i].val;
dfs_fi(y,x,d+1);
}
}
void add(int x,int f,int d,int v){
if(d<=(len>>1))mp[hs[d]]+=v;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
hs[d+1]=hs[d]*bas+e[i].val;
add(y,x,d+1,v);
}
}
inline bool jud(int lim){
return zs[lim>>1]==get(lim+1>>1,lim);
}
void dfs_se(int x,int t,int f,int d){
if(res)return ;
if(f==t)add(x,f,d,-1);
if(d>=(len+1>>1)&&mp[get(d-(len-d),d)]&&jud(d-(len-d)))res=true;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
hs[d+1]=hs[d]*bas+e[i].val;
zs[d+1]=zs[d]+((ull)e[i].val)*ba[d];
dfs_se(y,t,x,d+1);
}
if(f==t)add(x,f,d,1);
}
void sol(int x){
mp.clear();
dfs_fi(x,0,0);
dfs_se(x,x,0,0);
}
void dive(int x,int sz){
if(res)return ;
vis[x]=true;sol(x);
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;mx=n+1;
if(siz[y]<siz[x])findrt(y,x,siz[y]),dive(rt,siz[y]);
else findrt(y,x,sz-siz[x]),dive(rt,sz-siz[x]);
}
}
bool check(int mid){
memset(vis,false,sizeof(vis));
len=mid;res=false;mx=n+1;
findrt(1,0,n);dive(rt,n);
return res;
}
signed main(){
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
n=read();ba[0]=1;
fo(i,1,n)ba[i]=ba[i-1]*bas;
fo(i,1,n-1){
int x=read(),y=read(),z=read();
add_edg(x,y,z);add_edg(y,x,z);
}
int l=0,r=n>>1,mid;
while(l<r){
mid=l+r+1>>1;
if(check(mid*2))l=mid;
else r=mid-1;
}
ans=l*2;
l=0,r=n-1>>1;
while(l<r){
mid=l+r+1>>1;
if(check(mid*2+1))l=mid;
else r=mid-1;
}
ans=max(ans,l*2+1);
printf("%d",ans);
return 0;
}
T2 梧桐依旧
这竟然是个群论,我考场上一直认为这个东西和矩阵求逆有关系的
于是乎不想再解释啥,因为不会
这个可以看成是burnside引理的逆用了吧
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=3e7+5;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,p,pw[N],pk[N];
int ans,res1=1,res2,tmp;
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();p=read();
pk[0]=pw[0]=1;pw[n+1]=1;
fo(i,1,n)pk[i]=pk[i-1]*p%mod;
fu(i,n,1)pw[i]=pw[i+1]*(pk[i]-1+mod)%mod;
tmp=ksm(pw[n],mod-2);
fo(i,1,n){
res1=res1*(pk[n]-pk[i-1]+mod)%mod;
res2=(res2+pw[n-i+1]*pw[i+1]%mod)%mod;
}
ans=res1*(res2*ksm(pw[1],mod-2)%mod+1)%mod;
printf("%lld",ans);
}
T3 卿且去
这个有两种做法,一种是要全部大于一半的数,这个不太好优化
一种是建图,然后发现要求的就是最长反链,等价于最小链覆盖
然后就可以做了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=1e5+5;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,sq,ans;
int p[N],cnt;
bool vis[N];
void init(){
fo(i,2,sq){
if(!vis[i])p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=sq;j++){
vis[i*p[j]]=true;
if(i%p[j]==0)break;
}
}
}
int pw[N],iv2;
int id1[N],id2[N],cid,w[2*N],g[2*N];
int S(int x,int y){
if(x<=p[y])return 0;
int id=x<=sq?id1[x]:id2[n/x];
int ret=(g[id]-y+mod)*iv2%mod;
for(int i=y+1;i<=cnt&&p[i]*p[i]<=x;i++){
int xx=p[i];
for(int j=1;xx<=x;xx*=p[i],j++){
if(xx==4)break;
ret=(ret+iv2*(S(x/xx,i)+(j!=1)))%mod;
}
}
return ret;
}
signed main(){
freopen("yyds.in","r",stdin);
freopen("yyds.out","w",stdout);
n=read();sq=sqrt(n);init();
iv2=ksm(2,mod-2);
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
w[++cid]=n/l;
if(w[cid]<=sq)id1[w[cid]]=cid;
else id2[n/w[cid]]=cid;
g[cid]=w[cid]-1;
}
fo(i,1,cnt)for(int j=1;j<=cid&&p[i]*p[i]<=w[j];j++){
int id=w[j]/p[i];id=id<=sq?id1[id]:id2[n/id];
g[j]=g[j]-(g[id]-i+1);
}
ans=(S(n,0)-2*S(n,1)%mod+mod)%mod;
printf("%lld",(ans*ksm(2,g[1])%mod+((n+1>>1)-1)%mod*ksm(2,g[1]))%mod);
}