【NOIp2019模拟】题解
作者:互联网
T1:
显然一定会询问n次
考虑如果能求得每个点的前缀异或和 就可以解出整个序列了
知道了sl,sr之间一个,就可以一次询问[l+1,r]求出另外一个
考虑把r向l连边,所有点向0连边
那最后其实要的就是整个图联通的代价
每个点代价设为前缀异或和
问题就变成了最小异或生成树
最高位往最低位贪心就可以了
#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:
考虑一个显然的dp
f[i][j][s]表示长度为i,值模k为j,S限制的数每个%3为多少的状态
显然有f[a+b][i][j]=∑(x∗10b+y)%k=i,p⊕q=jf[a][x][p]∗f[b][y][q]
⊕为三进制下不进位加法
由于没有前置0的限制
可以直接倍增dp拿30pts
然后第一维x可以把位置先处理出来
第一维是一个循环卷积
第二维是一个三进制FWT
用石家庄的工人阶级队伍那道题的做法就可以
然后相当于是一个高维卷积
做法是相当于一个矩阵
先把行转点值
然后把每一列提出来转点值
然后把2个矩阵对应点乘起来
再把列转回去,行转回去
吐槽一句:
考场上的时候已经把高维卷积推出来了
但是FWT写挂了,把一个a2写成a1了
然后没看出来
就没去写FFT
下午改题的时候lim开小又调了一下午
我真的是好难啊
#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]表示在A中边i的儿子(相当于带了个顺序),子树哈希情况为j的方案数
再记一个g表示B中当前点几个子树选择情况为k的方案数
然后xjb记忆化dp一波就完了
不过由于同构子树可能重复计数
题解是把状态从大往小计算,然后强制B中相同的要依次选
数组f要开够
#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