其他分享
首页 > 其他分享> > 省选模拟11

省选模拟11

作者:互联网

啊!终于改完题了!!!

说实话,考试的时候睡着了好几次,于是只有暴力分

很困的,没有办法思考...................................................................................

但是我一眼看出来第一题一定是个点分治,然后第二题只会打暴力

第三题蒙了个结论然后爆蛋了......

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);
}

标签:11,ch,return,省选,hs,int,while,模拟,mod
来源: https://www.cnblogs.com/hzoi-fengwu/p/15867730.html