其他分享
首页 > 其他分享> > 【NOIp2019模拟】题解

【NOIp2019模拟】题解

作者:互联网

T1:

显然一定会询问nnn次
考虑如果能求得每个点的前缀异或和 就可以解出整个序列了

知道了sl,srs_l,s_rsl​,sr​之间一个,就可以一次询问[l+1,r][l+1,r][l+1,r]求出另外一个
考虑把rrr向lll连边,所有点向000连边
那最后其实要的就是整个图联通的代价
每个点代价设为前缀异或和

问题就变成了最小异或生成树
最高位往最低位贪心就可以了

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
const int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a=add(a,b);}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline void Dec(int &a,int b){a=dec(a,b);}
inline int mul(int a,int b){return 1ll*a*b>=mod?1ll*a*b%mod:a*b;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int ksm(int a,int b,int res=1){for(;b;b>>=1,a=mul(a,a))(b&1)?(res=mul(res,a)):0;return res;}
inline void chemx(ll &a,ll b){a<b?a=b:0;}
inline void chemn(ll &a,ll b){a>b?a=b:0;}
cs int N=100005,Log=31;
int son[N*Log][2],siz[N*Log],tot;
vector<int> p[N*Log];
#define lc son[u][0]
#define rc son[u][1]
void insert(int v){
	int u=0;
	for(int i=Log-1;~i;i--){
		int t=(v&(1<<i))?1:0;
		if(!son[u][t])son[u][t]=++tot;
		u=son[u][t],p[u].pb(v);
	}
}
inline ll query(int u,int v,int dep){
	ll res=0;
	for(int i=dep;~i;i--){
		int t=(v&(1<<i))?1:0;
		if(son[u][t])u=son[u][t];
		else u=son[u][t^1],res+=1<<i;
	}
	return res;
}
ll build(int u,int dep){
	ll res=0,mn=1e9;
	if(lc)res+=build(lc,dep-1);
	if(rc)res+=build(rc,dep-1);
	if(lc&&rc){
		int a,b;
		if(p[lc].size()>p[rc].size())a=lc,b=rc;
		else a=rc,b=lc;
		for(int &v:p[b])chemn(mn,query(a,v,dep-1));
		res+=mn+(1<<dep);
	}
	return res;
}
int n,a[N];
int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read()^a[i-1];
	for(int i=0;i<=n;i++)insert(a[i]);
	cout<<build(0,Log-1);
}

T2:

考虑一个显然的dpdpdp
f[i][j][s]f[i][j][s]f[i][j][s]表示长度为iii,值模kkk为jjj,SSS限制的数每个%3\%3%3为多少的状态

显然有f[a+b][i][j]=(x10b+y)%k=i,pq=jf[a][x][p]f[b][y][q]f[a+b][i][j]=\sum_{(x*10^{b}+y)\%k=i,p\oplus q=j}f[a][x][p]*f[b][y][q]f[a+b][i][j]=∑(x∗10b+y)%k=i,p⊕q=j​f[a][x][p]∗f[b][y][q]
\oplus⊕为三进制下不进位加法
由于没有前置0的限制
可以直接倍增dpdpdp拿30pts30pts30pts

然后第一维xxx可以把位置先处理出来
第一维是一个循环卷积
第二维是一个三进制FWTFWTFWT
用石家庄的工人阶级队伍那道题的做法就可以

然后相当于是一个高维卷积
做法是相当于一个矩阵
先把行转点值
然后把每一列提出来转点值
然后把2个矩阵对应点乘起来
再把列转回去,行转回去

吐槽一句:
考场上的时候已经把高维卷积推出来了
但是FWTFWTFWT写挂了,把一个a2a2a2写成a1a1a1了
然后没看出来
就没去写FFTFFTFFT

下午改题的时候limlimlim开小又调了一下午
我真的是好难啊

#include<bits/stdc++.h>
using namespace std;
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
const int mod=998244353,G=3;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a=add(a,b);}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline void Dec(int &a,int b){a=dec(a,b);}
inline int mul(int a,int b){return 1ll*a*b>=mod?1ll*a*b%mod:a*b;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a))(b&1)?(res=mul(res,a)):0;return res;}
inline int ksm(int a,ll b,int Mod){
	int res=1;
	for(;b;b>>=1,a=1ll*a*a%Mod)
		if(b&1)res=1ll*res*a%Mod;
	return res;
}
inline void chemx(int &a,int b){a<b?a=b:0;}
inline void chemn(int &a,int b){a>b?a=b:0;}
cs int N=555,C=730;
int S,k,bin[N],sta,num,vis[N];
ll n;
struct plx{
	int x,y;
	plx(int _x=0,int _y=0):x(_x),y(_y){}
	friend inline plx operator +(cs plx &a,cs plx &b){
		return plx(add(a.x,b.x),add(a.y,b.y));
	}
	friend inline plx operator -(cs plx &a,cs plx &b){
		return plx(dec(a.x,b.x),dec(a.y,b.y));
	}
	friend inline plx operator *(cs plx &a,cs plx &b){
		return plx(dec(mul(a.x,b.x),mul(a.y,b.y)),dec(add(mul(a.x,b.y),mul(a.y,b.x)),mul(a.y,b.y)));
	}
	friend inline plx operator *(cs plx &a,cs int &b){
		return plx(mul(a.x,b),mul(a.y,b));
	}
	void operator+=(cs plx &b){*this=*this+b;}
	void operator-=(cs plx &b){*this=*this-b;}
	void operator*=(cs plx &b){*this=*this*b;}
	void operator*=(int b){Mul(x,b),Mul(y,b);}
};
plx w1,w2;
inline void DFT(plx *f,int lim){
	plx a0,a1,a2;
	for(int mid=1;mid<lim;mid*=3)
	for(int i=0;i<lim;i+=(mid*3))
	for(int j=0;j<mid;j++)
	a0=f[i+j],a1=f[i+j+mid],a2=f[i+j+mid*2],
	f[i+j]=a0+a1+a2,f[i+j+mid]=a0+a1*w1+a2*w2,f[i+j+mid*2]=a0+a1*w2+a2*w1;
}
inline void IDFT(plx *f,int lim){
	plx a0,a1,a2;
	for(int mid=1;mid<lim;mid*=3)
	for(int i=0;i<lim;i+=(mid*3))
	for(int j=0;j<mid;j++)
	a0=f[i+j],a1=f[i+j+mid],a2=f[i+j+mid*2],
	f[i+j]=a0+a1+a2,f[i+j+mid]=a0+a1*w2+a2*w1,f[i+j+mid*2]=a0+a1*w1+a2*w2;
	for(int i=0,inv=ksm(lim,mod-2);i<lim;i++)Mul(f[i].x,inv);
}
cs int c=12;
vector<int> w[c+1];
inline void init_w(){
	for(int i=1;i<=c;i++)w[i].resize(1<<(i-1));
	int wn=ksm(G,(mod-1)/(1<<c));
	w[c][0]=1;
	for(int i=1;i<(1<<(c-1));i++)w[c][i]=mul(w[c][i-1],wn);
	for(int i=c-1;i;i--)
	for(int j=0;j<(1<<(i-1));j++)
	w[i][j]=w[i+1][j<<1];
}
int rev[N<<2],lim;
inline void init_rev(int k){
	lim=1;
	while(lim<=(k*2))lim<<=1;
	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(lim>>1));
}
inline void ntt(plx *f,int kd){
	plx a0,a1;
	for(int i=0;i<lim;i++)if(i<rev[i])swap(f[i],f[rev[i]]);
	for(int mid=1,l=1;mid<lim;mid<<=1,l++)
	for(int i=0;i<lim;i+=(mid<<1))
	for(int j=0;j<mid;j++)
	a0=f[i+j],a1=f[i+j+mid]*w[l][j],f[i+j]=a0+a1,f[i+j+mid]=a0-a1;
	if(kd==-1){
		reverse(f+1,f+lim);
		for(int i=0,inv=ksm(lim,mod-2);i<lim;i++)f[i]=f[i]*inv;
	}
}
inline void mul(plx *tmp,plx *a,plx *b,ll len){
	static plx A[N<<1],B[N<<1];
	int mo=ksm(10,len,k);
	for(int i=0;i<lim;i++)A[i]=B[i]=plx(0,0);
	for(int i=0;i<k;i++)A[i*mo%k]+=a[i],B[i]=b[i];
	ntt(A,1),ntt(B,1);
	for(int i=0;i<lim;i++)A[i]*=B[i];
	ntt(A,-1);
	for(int i=k;i<lim;i++)A[i%k]+=A[i];
	for(int i=0;i<k;i++)tmp[i]=A[i];
}
plx res[N<<2];
inline plx LpMul(plx *f,ll b){
	for(int i=0;i<k;i++)res[i]=plx(0,0);
	res[0].x=1;
	for(ll len=1;b;b>>=1,mul(f,f,f,len),len*=2)
	if(b&1)mul(res,res,f,len);
	return res[0];
}
plx f[N][C],A[N<<2],F[C];
char s[N];
int main(){
	cin>>n,k=read(),init_w(),init_rev(k);
	bin[0]=1;w1=plx(0,1),w2=plx(mod-1,mod-1);
	for(int i=1;i<=9;i++)bin[i]=bin[i-1]*3;
	scanf("%s",s);
	S=strlen(s);sta=pow(3,S);
	for(int i=0;i<S;i++)vis[s[i]-'0']=++num;
	for(int i=0;i<=9;i++)f[i%k][bin[vis[i]]/3].x+=1;
	for(int i=0;i<k;i++)DFT(f[i],sta);
	for(int i=0;i<sta;i++){
		for(int j=0;j<k;j++)
		A[j]=f[j][i];
		F[i]=LpMul(A,n);
	}
	IDFT(F,sta);
	cout<<F[0].x<<'\n';
}

T3:

树的哈希显然就找在重心为根乱搞一波哈希

然后设f[i][j]f[i][j]f[i][j]表示在AAA中边iii的儿子(相当于带了个顺序),子树哈希情况为jjj的方案数
再记一个ggg表示BBB中当前点几个子树选择情况为kkk的方案数
然后xjbxjbxjb记忆化dpdpdp一波就完了

不过由于同构子树可能重复计数
题解是把状态从大往小计算,然后强制BBB中相同的要依次选

数组fff要开够

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
const int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a=add(a,b);}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline void Dec(int &a,int b){a=dec(a,b);}
inline int mul(int a,int b){return 1ll*a*b>=mod?1ll*a*b%mod:a*b;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int ksm(int a,int b,int res=1){for(;b;b>>=1,a=mul(a,a))(b&1)?(res=mul(res,a)):0;return res;}
inline void chemx(int &a,int b){a<b?a=b:0;}
inline void chemn(int &a,int b){a>b?a=b:0;}
cs int N=2005,M=14,bas=131,mod1=4178981348;
vector<int> e1[N],e2[N],e3[N],son1[N],h[N];
map<ll,int> mp;
int n,m,rt1,rt2,siz[N],son[N],id[N],t[N];
int vis[N][N],f[N*2][N],num,tot,ans;
void dfs(int u,int fa){
	siz[u]=1,son[u]=0;
	for(int &v:e2[u]){
		if(v==fa)continue;
		dfs(v,u);
		siz[u]+=siz[v];
		son[u]=max(son[u],siz[v]);
	}
	son[u]=max(son[u],m-siz[u]);
	if(son[u]<son[rt1])rt1=u,rt2=0;
	else if(son[u]==son[rt1])rt2=u;
}
void HasT(int u,int fa){
	ll has=0;
	for(int &v:e3[u]){
		if(v==fa)continue;
		HasT(v,u);
		son1[u].pb(t[v]);
	}
	sort(son1[u].bg(),son1[u].end());
	for(int &x:son1[u])has=(has*bas^x)%mod;
	if(!mp.count(has))h[++tot]=son1[u],mp[has]=tot;
	t[u]=mp[has];
}
int calc(int u,int fa,int tt){
	if(!vis[u][fa])vis[u][fa]=++num;
	int id=vis[u][fa];
	if(f[id][tt])return f[id][tt];
	vector<int> g;g.resize(1<<h[tt].size());
	g[0]=1;
	for(int &v:e1[u]){
		if(v==fa)continue;
		for(int s=1<<h[tt].size();~s;s--)if(g[s])
		for(int k=0;k<h[tt].size();k++)
		if(!(s&(1<<k))&&(!k||(s&(1<<(k-1)))||h[tt][k]!=h[tt][k-1]))
		Add(g[s+(1<<k)],mul(g[s],calc(v,u,h[tt][k])));
	}
	f[id][tt]=g[(1<<h[tt].size())-1];
	return f[id][tt];
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		e1[u].pb(v),e1[v].pb(u);
	}
	m=read();
	for(int i=1;i<m;i++){
		int u=read(),v=read();
		e2[u].pb(v),e2[v].pb(u);
	}
	son[0]=m,dfs(1,0);
	if(rt2){
		if(rt1>rt2)swap(rt1,rt2);
		for(int i=1;i<=m;i++)
			for(int &v:e2[i]){
				if(i<v&&(i!=rt1||v!=rt2))e3[i].pb(v),e3[v].pb(i);
			}
		m++,e3[m].pb(rt1),e3[rt1].pb(m);
		e3[m].pb(rt2),e3[rt2].pb(m),rt1=m;
	}
	else for(int i=1;i<=m;i++)
	for(int &v:e2[i])if(i<v)e3[v].pb(i),e3[i].pb(v);
	HasT(rt1,0);
	if(rt2){
		for(int i=1;i<=n;i++)
		for(int &v:e1[i])if(i<v){
			Add(ans,mul(calc(i,v,h[t[m]][0]),calc(v,i,h[t[m]][1])));
			if(h[t[m]][0]!=h[t[m]][1])
			Add(ans,mul(calc(i,v,h[t[m]][1]),calc(v,i,h[t[m]][0])));
		}
	}
	else for(int i=1;i<=n;i++)Add(ans,calc(i,0,t[rt1]));
	cout<<ans;
}

标签:int,题解,void,NOIp2019,plx,mul,res,inline,模拟
来源: https://blog.csdn.net/qq_42555009/article/details/100147240