正睿 青岛集训 Day4 Test
2019.3.5 青岛普转肯模拟赛day1暨徐源粉丝线下见面会
期望得分:70+20+40
实际得分:30+20+20
A.智慧树tree(树形DP NTT Bluestein)
根据群里\(dalao\)的聊天记录,瞎猜理解了一波,重新理解了一遍DFT和NTT...会记在下面(\(dalao\)就忽略吧)(我当时是学了些啥啊
)。
首先\(70\)分是直接用\(NTT\)优化背包(\(dp_i=\left(\prod_{v\in son_i}dp_v+1\right)\cdot x^{a_i}\))。就是每次转成点值,对应相乘,再转回去。复杂度是\(O(nm\log m)\)。
蒟蒻表示虽然知道能这么做,但是不明白为什么做模\(m\)的循环卷积,这样还是对的。重学了一下,因为我们代的是\(m\)次单位根,如果单位根的次数\(\geq m\),就又回去了,所以点值乘的时候就是对\(m\)取模的。
满分做法当然是,DP的过程中不转回系数表示,直接用点值表示做。统计答案时把点值累加,最后\(IDFT\)回去。
转移是\(dp_i(\omega_m^k)=\left(\prod_{v\in son_i}dp_v(\omega_m^k)+1\right)(\omega_m^k)^{a_i}\),那个\(+1\)是个常数(虽然是在系数表示下的\(+1\)),所以直接在点值处\(+1\)就可以了。
最初的正变换是\(n\)次单点的\(DFT\),直接每次代进\(m\)个单位根\(O(m)\)转成点值表示就可以了(\(A_i(\omega^k)=(\omega^k)^{a_i}\)),复杂度是\(O(nm)\)的。
注意到\(m\)不是\(2\)的次幂,而朴素\(NTT\)必须将多项式长度\(m\)补成\(2^k\),但是这样代的单位根就不是\(m\)次单位根了。也就是多项式长度必须是\(m\)。
任意长度的\(DFT\)需要用到\(Bluestein's\ Algorithm\),下面写。
补充一点...当时学\(NTT\)没注意(或者忘掉)的东西。。
令\(n=2^k\),那么取素数\(p=r\cdot2^k+1\)(满足这样形式的素数叫费马素数,朴素\(NTT\)必须是这样的模数,因为要存在任意\(2^i\ (i\leq k)\)的单位根),那么\(p\)存在\(n\)次单位根,且\(\omega_n=g^{\frac{p-1}{n}}\)。
注意到这题模数\(p=950009857\)存在原根,且\(m=2^k,49152,57984\)都是\(p-1\)的约数,所以存在对应的\(m\)次单位根。
然而后几个点的\(m\neq2^k\),不能直接\(NTT\)(上面提到了),需要\(Bluestein\)。但是各位\(dalao\)说可以快速插值或者多点求值。。
学了下(也没学,就是看了下是干嘛的),大概就是,用点值DP完之后,我们可以得到\(m\)个点值,快速插值就可以得到原多项式的系数表示了。
注意到\(IDFT\)实际就是,将\(m\)次单位根的逆元代入点值出来的多项式,以得到原多项式的系数表示(\(c_k=\frac1m\sum\limits_{i=0}^{m-1}A(\omega_m^i)(\omega_m^{-i})^k\))。
所以也可以把点值的逆元代进去,多点求值就可以了。。
然而快速插值和多点求值都巨难写,而且常数大的恐怖吧。。(这辈子是不会写的.jpg)
正变换是\(O(nm)\)的,中间过程只有点值相乘也是\(O(nm)\)的,最后每个点用\(Bluestein\) \(IDFT\)回去是\(O(m\log m)\)的。
然后,记一下\(Bluestein's\ Algorithm\),用以解决任意长度\(DFT\)。更多相关题目戳这。
考虑\(DFT\)的形式:
注意到\(\sum\)是个卷积,可以用\(FFT/NTT\)计算。所以\(Bluestein\)的复杂度是\(O(n\log n)\)的。
具体:\(k-i\)可能是负的,所以对后一项右移\(n\)位,令\(f_i=a_i\omega_{2n}^{i^2},\ g_i=\omega_{2n}^{-(i-n)^2}\),那么\(y_k=\omega_{2n}^{k^2}\sum_{i}f_ig_{n+k-i}=\omega_{2n}^{k^2}(f\times g)_{n+k}\)。
\(IDFT\)同理,可以直接令\(\omega_{2n}=\omega_{2n}^{-1}\),代到\(DFT\)的式子里,也可以一样的推一下:
令\(f_i=a_i\omega_{2n}^{i^2},\ g_i=\omega_{2n}^{-(2n-1-i)^2}\),那么\(c_k=\frac{1}{n}\omega_{2n}^{k^2}\sum_if_ig_{2n-1-k-i}=\omega_{2n}^{k^2}(f\times g)_{2n-1-k}\)。
上面是一般的做法(其实就是个\(trick\)),但是\(dls\)指出有更好一些的做法:
像这样写成平方需要\(\omega_{2n}\)(有些题可能不存在\(2n\)次单位根),就可以用:\(ij=\binom{i+j}{2}-\binom i2-\binom j2\)来替换:\(y_k=\omega_n^{-\binom k2}\sum_{i=0}^{n-1}a_i\omega_n^{-\binom i2}\omega_n^{\binom{i+j}{2}}\)。
随便扯一句废话:\(m\)必须要有单位根才能做(直接\(FFT\)做DP 精度会爆炸)
最后,还有个问题是卡内存。
\(DFS\)的时候先\(DFS\)重儿子,然后父节点可以直接继承重儿子的DP数组,再\(DFS\)轻儿子。这样我们只需要记录当前\(O(轻链个数)\)个\(=O(\log n)\)个DP数组。
关于类似的卡内存,有一道扩展题:
给定一棵\(n\)个点的树,每个点有一个重量和价值,你要选一个独立集,对于每个重量\(1,2,...,m\),求出其最大价值和。要求复杂度\(O(n^2m)\)。
先DFS轻儿子子树,再考虑父节点,再考虑重儿子...状压现在有用的点的状态...(没听懂.jpg,望dalao解答)
做到了是这道题。
//8523ms 6080kb
#include <cstdio>
#include <cctype>
#include <algorithm>
#define G 7
#define invG 135715694
#define mod 950009857
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
#define gc() getchar()
#define MAXIN 200000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=8005,M=58000,M2=(1<<18)+1/*3m*/,S=30;
int n,m,A[N],W[M<<1],Enum,H[N],nxt[N<<1],to[N<<1],sz[N],son[N],top,sk[S],id[N],f[S][M],g[M2],h[M2],rev[M2],lim;
LL Ans[M];
bool vis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int FP(int x,int k)
{
int t=1;
for(; k; k>>=1,x=1ll*x*x%mod)
if(k&1) t=1ll*t*x%mod;
return t;
}
void NTT(int *a,int lim,int opt)
{
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1,Wn=FP(~opt?G:invG,(mod-1)/i);
for(int j=0; j<lim; j+=i)
for(int k=j,w=1,t; k<j+mid; ++k,w=1ll*w*Wn%mod)
a[k+mid]=a[k]+mod-(t=1ll*a[k+mid]*w%mod), Mod(a[k+mid]),
a[k]+=t, Mod(a[k]);
}
if(opt==-1) for(int i=0,inv=FP(lim,mod-2); i<lim; ++i) a[i]=1ll*a[i]*inv%mod;
}
void DFS1(int x,int fa)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa) DFS1(v,x), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
}
inline void Init(int x,int m)
{
vis[x]=1; int *fx=f[id[x]=sk[top--]],a=A[x];
for(int i=0,x=0; i<m; ++i,(x+=a)>=m&&(x-=m)) fx[i]=W[x];
}
inline void Merge(int x,int *fv,int m)
{
if(!vis[x]) Init(x,m);
int *fx=f[id[x]];
for(int i=0; i<m; ++i) fx[i]=1ll*fx[i]*(fv[i]+1)%mod;
}
void DFS2(int x,int fa)
{
if(son[x]) DFS2(son[x],x);
else Init(x,m);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa&&v!=son[x]) DFS2(v,x);
int *fx=f[id[x]];
for(int i=0; i<m; ++i) Ans[i]+=fx[i];
if(fa) Merge(fa,fx,m);
sk[++top]=id[x];
}
void Bluestein(const int n)
{
int n2=n<<1,w=FP(G,(mod-1)/n2); W[0]=1, W[1]=w;
for(int i=2; i<n2; ++i) W[i]=1ll*W[i-1]*w%mod;
for(int i=0; i<n; ++i) g[i]=1ll*g[i]*W[1ll*i*i%n2]%mod;
for(int i=0; i<n2; ++i) h[i]=FP(W[1ll*(n2-1-i)*(n2-1-i)%n2],mod-2);
int lim=1,l=-1; while(lim<n*3) lim<<=1, ++l;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
NTT(g,lim,1), NTT(h,lim,1);
for(int i=0; i<lim; ++i) g[i]=1ll*g[i]*h[i]%mod;
NTT(g,lim,-1);
for(int i=0,inv=FP(n,mod-2); i<n; ++i) printf("%d ",1ll*inv*g[n2-1-i]%mod*W[1ll*i*i%n2]%mod);
}
int main()
{
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
const int n=read(),m=read(); ::m=m;
for(int i=1; i<=n; ++i) A[i]=read();
for(int i=1; i<n; ++i) AE(read(),read());
int w=FP(G,(mod-1)/m); W[0]=1, W[1]=w;
for(int i=2; i<m; ++i) W[i]=1ll*W[i-1]*w%mod;
for(int i=1; i<S; ++i) sk[++top]=i;
DFS1(1,0), DFS2(1,0);
for(int i=0; i<m; ++i) g[i]=Ans[i]%mod;
int lim=1,l=-1; while(lim<m) lim<<=1, ++l;
if(lim==m)
{
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
NTT(g,lim,-1);
for(int i=0; i<m; ++i) printf("%d ",g[i]);
}
else Bluestein(m);
return 0;
}
B.组合数combination(数位DP Lucas)
\(Description\)
给定\(n,m,p,l_1,r_1,...,l_n,r_n\),求\(\sum_{i_1=l_1}^{r_1}\sum_{i_2=l_2}^{r_2}...\sum_{i_n=l_n}^{r_n}C_m^{i_1+i_2+...+i_n}mod\ p\)。
\(n\leq 7,\ m\leq 10^{18},\ l_i\leq r_i\leq 10^{17},\ p\leq 10\)且\(p\)为质数。
\(Solution\)
直接考虑\(Lucas\):
区间限制可以\(2^n\)容斥成只有上界的限制,然后就是数位DP了(虽然不明显,但是考虑是\(P\)进制各位独立以及\(P=2\)的\(Subtask\)应该也可以想到)。因为每进制位上是互不影响的,所以当前贡献就是乘个\(\binom{n\%m}{sum\%p}\),然后处理下一位。
考虑一些简单情况:\(n=2,P=2\),转移时每一位枚举两个数填什么以及是否需要进\(1\)的位即可。状态就记三维\(0/1\):第一二个数是否卡上界、是否进位。
当\(n>2\)时,同样每一位枚举每个数填什么即可,上界限制直接记\(2^n\)的状态。
当\(P>2\)时,就直接对每一位记进了多少的数。有\(n\)个数就先递归\(n\)次再枚举下一位,就是很暴力的开\(lim\times n\times n\times n\times2^n\)的数组表示第几位、第几个数、这一位前几个数的和是多少、进了多少、上界限制。
发现像\(P=2\)时从高位到低位做,每次转移枚举进了多少不太靠谱,考虑从低位到高位就没这个问题了。只需要处理一下上界的状态:在低位如果有第\(i\)个数超过上界,令\(s[i]=1\);如果在高位第\(i\)个数小于上界,令\(s[i]=0\)。最后如果所有的\(s[i]=0\)就是合法的(没有超上界的数)。
PS:其实也不需要"每次转移枚举进了多少",每一位先枚举\(sum\)是多少,再枚举\(i_1,i_2,...\)填什么,这样就可以从高位往低位转移了...
复杂度...因为有个\(\log_p10^{17}\),要么\(P=2\)要么\(P=7\)最劣吧...
容斥+压状态有个\(4^n\),但是容斥可以去掉,压是否卡上界卡下界。可能同时卡上下界,但是这种情况是不会同时出现的...?所以去掉\(0\)的状态用哈希表存状态就是\(3^n\)的了。
注意最高位的取值\(lim\)要加一,因为最后是可能会再进一位的。
其实会\(P=2\)和\(Lucas\)就很好想,但是真这么暴力的吗= = 不敢写.jpg
//19789ms 8004kb
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=7,M=62;
int n,P,lim,bitn[M],A[N][M],C[N][N],Time,f[M][N][N][N][1<<N],vis[M][N][N][N][1<<N];
LL m,L[N],R[N];
inline LL read()
{
LL now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
void Divide(int *bit,LL x)
{
memset(bit,0,lim<<2);
for(int t=0; x; x/=P) bit[t++]=x%P;
}
int DFS(int x,int num,int sum,int carry,int s)
{
if(x==lim) return !s;
if(num==n) return C[bitn[x]][sum]*DFS(x+1,0,carry%P,carry/P,s)%P;
int &vis=::vis[x][num][sum][carry][s],&ans=f[x][num][sum][carry][s];
if(vis==Time) return ans;
vis=Time, ans=0;
for(int i=P-1; ~i; --i)
{
int ss=s;
if(i>A[num][x]) ss|=1<<num;
else if(ss>>num&1 && i<A[num][x]) ss^=1<<num;
bool f=sum+i>=P;
ans+=DFS(x,num+1,f?sum+i-P:sum+i,carry+f,ss);
}
return ans%P;
}
int main()
{
// freopen("combination.in","r",stdin);
// freopen("combination.out","w",stdout);
n=read(),m=read(),P=read();
for(int i=0; i<P; ++i)
{
C[i][0]=C[i][i]=1;
for(int j=1; j<i; ++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
LL mx=m;
for(int i=0; i<n; ++i) L[i]=read(),mx=std::max(mx,R[i]=read());
for(LL t=1; t<=mx; t*=P,++lim); ++lim;
Divide(bitn,m);
int ans=0;
for(int s=0,l=1<<n; s<l; ++s)
{
bool f=0;
for(int i=0; i<n; ++i)
if(s>>i&1) f^=1, Divide(A[i],L[i]-1);
else Divide(A[i],R[i]);
++Time, ans+=(f?-1:1)*DFS(0,0,0,0,0);
}
printf("%d\n",(ans%P+P)%P);
return 0;
}
C
考试代码
A
考试代码:
#include <map>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define Iter std::map<int,int>::iterator//auto
#define mod 950009857
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
#define Add2(x,y) x+y>=m?x+y-m:x+y
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
//typedef unsigned int uint;
const int N=8005,M=58000;
int n,m,A[N],Enum,H[N],nxt[N<<1],to[N<<1];
LL Ans[M];
std::map<int,int> f[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
namespace Subtask1
{
int f[102][152],g[152];
LL Ans[152];
void DFS(int x,int fa)
{
int *fx=f[x]; fx[A[x]]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa)
{
DFS(v,x);
int *fv=f[v]; ++fv[0];
for(int a=0,v1; a<m; ++a)
if((v1=f[v][a]))
for(int b=0,p; b<m; ++b)
p=Add2(a,b), Add(g[p],1ll*fx[b]*v1%mod);
for(int a=0; a<m; ++a) fx[a]=g[a], g[a]=0;
}
// printf("%d:\n",x);
// for(int i=0; i<m; ++i) printf("f[%d]=%d\n",i,fx[i]); puts("");
for(int i=0; i<m; ++i) Ans[i]+=fx[i];
}
void Main(int n,int m)
{
DFS(1,1);
for(int i=0; i<m; ++i) printf("%d ",(int)(Ans[i]%mod));
}
}
void DFS(int x,int fa)
{
static int g[M];//map
static pr tmp[M];
std::map<int,int> &fx=f[x]; fx[A[x]]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa)
{
DFS(v,x);
std::map<int,int> &fv=f[v]; ++fv[0];
int t=0;
for(Iter it2=fx.begin(); it2!=fx.end(); ++it2)
if(it2->second) tmp[++t]=mp(it2->first,it2->second);
int a,v1,p;
for(Iter it=fv.begin(); it!=fv.end(); ++it)
if((a=it->first,v1=it->second))
for(int j=1; j<=t; ++j)
p=Add2(a,tmp[j].first), Add(g[p],1ll*tmp[j].second*v1%mod);
for(int a=0; a<m; ++a) if(g[a]) fx[a]=g[a], g[a]=0;
}
for(Iter it=fx.begin(); it!=fx.end(); ++it) Ans[it->first]+=it->second;
}
int main()
{
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
const int n=read(); ::n=n,m=read();
for(int i=1; i<=n; ++i) A[i]=read();
for(int i=1; i<n; ++i) AE(read(),read());
if(n<=100&&m<=150) return Subtask1::Main(n,m),0;
DFS(1,1);
for(int i=0; i<m; ++i) printf("%d ",(int)(Ans[i]%mod));
return 0;
}
70分代码:
#include <cstdio>
#include <cctype>
#include <algorithm>
#define G 7
#define invG 135715694
#define mod 950009857
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=8005,M=58000;
int n,m,A[N],Enum,H[N],nxt[N<<1],to[N<<1],dis[N],pre[N];
int Tot,top,sk[N],id[N],lim,Inv,rev[N],f[2002][32769];
LL Ans[M];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int FP(int x,int k)
{
int t=1;
for(; k; k>>=1,x=1ll*x*x%mod)
if(k&1) t=1ll*t*x%mod;
return t;
}
int BFS(int x)
{
static int q[N];
int h=0,t=1; q[0]=x, dis[x]=pre[x]=0;
while(h<t)
{
int x=q[h++];
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=pre[x]) pre[v]=x, dis[v]=dis[x]+1, q[t++]=v;
}
return q[t-1];
}
int FindRoot()
{
int T=BFS(1),S=BFS(T);
for(int x=dis[S]>>1; x; --x,S=pre[S]);
return S;
}
void NTT(int *a,int lim,int opt)
{
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1,Wn=FP(~opt?G:invG,(mod-1)/i);
for(int j=0; j<lim; j+=i)
for(int k=j,w=1,t; k<j+mid; ++k,w=1ll*w*Wn%mod)
a[k+mid]=a[k]+mod-(t=1ll*a[k+mid]*w%mod), Mod(a[k+mid]),
a[k]+=t, Mod(a[k]);
}
if(opt==-1)
for(int i=0,inv=Inv; i<lim; ++i) a[i]=1ll*a[i]*inv%mod;
}
void DFS(int x,int fa)
{
int p=top?sk[top--]:++Tot,*fx=f[p]; id[x]=p;
for(int i=0; i<m; ++i) fx[i]=0;
fx[A[x]]=1, NTT(fx,lim,1);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa)
{
DFS(v,x);
int *fv=f[id[v]]; ++fv[0];
NTT(fv,lim,1);
for(int j=0; j<m; ++j) fx[j]=1ll*fx[j]*fv[j]%mod;
}
sk[++top]=p;
NTT(fx,lim,-1);
for(int i=0; i<m; ++i) Ans[i]+=fx[i];
}
int main()
{
freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
const int n=read(),m=read(); ::n=n,::m=m;
for(int i=1; i<=n; ++i) A[i]=read();
for(int i=1; i<n; ++i) AE(read(),read());
int lim=1,l=-1;
while(lim<m) lim<<=1, ++l;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
::lim=lim, Inv=FP(lim,mod-2), DFS(FindRoot(),0);
for(int i=0; i<m; ++i) printf("%d ",(int)(Ans[i]%mod));
return 0;
}
B
数位DP用到了\(C(n,m)为奇数\Leftrightarrow n\&m=m\)。
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define Mod(x) x>=mod&&(x-=mod)
#define gc() getchar()
typedef long long LL;
const int N=9,LIM=1e9;
LL L[N],R[N];
inline LL read()
{
LL now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now*f;
}
//#define De 0
namespace Subtask4//P==2
{
const int N=70;
int cntn,cntx,cnty,bitn[N],bitx[N],bity[N],f[N][8];
bool vis[N][8];
int DFS(int x,int s)//limx limy 1:carry
{
// De&&printf("x:%d %d,%d,%d\n",x,s&1,(s&2),(s&4));
if(!x) return (s&4)==0;
if(vis[x][s]) return f[x][s];
int ans=0,upx=s&1?bitx[x]:1,upy=s&2?bity[x]:1,car=s&4;
for(int i=0; i<=upx; ++i)
for(int j=0; j<=upy; ++j)
for(int k=0; k<=1; ++k)
{
if((car&&i+j+k<2)||(!car&&i+j+k>1)) continue;
int tmp=i+j+k&1;
if((bitn[x]&tmp)!=tmp) continue;
// De&&printf("x:%d i:%d j:%d k:%d val:%d\n",x,i,j,k,DFS(x-1,(s&1&&i==upx)|((s&2&&j==upy)<<1)|(k<<2)));
ans+=DFS(x-1,(s&1&&i==upx)|((s&2&&j==upy)<<1)|(k<<2));
}
// De&&printf("After x:%d %d,%d,%d ans:%d\n",x,s&1,(s&2),(s&4),ans);
return vis[x][s]=1,f[x][s]=ans&1;
}
int Calc(LL x,LL y)
{
// De&&printf("\nX:%lld Y:%lld\n",x,y);
cntx=cnty=0;
memset(vis,0,sizeof vis);
memset(bitx,0,sizeof bitx);
memset(bity,0,sizeof bity);
for(LL t=x; t; t>>=1) bitx[++cntx]=t&1;
for(LL t=y; t; t>>=1) bity[++cnty]=t&1;
int l=std::max(cntn,std::max(cntx,cnty));
// De&&printf("res: %d %d\n",DFS(l,3),DFS(l,7));
// if(De) return 0;
return DFS(l,3);
}
void Main(int n,LL m)
{
cntn=0, memset(bitn,0,sizeof bitn);
for(LL x=m; x; x>>=1) bitn[++cntn]=x&1;
int ans;
if(n==1) ans=2+Calc(R[1],0)-Calc(L[1]-1,0);
else ans=32+Calc(R[1],R[2])-Calc(R[1],L[2]-1)-Calc(L[1]-1,R[2])+Calc(L[1]-1,L[2]-1);
printf("%d\n",ans&1);
}
}
namespace TEMP
{
const int N=4e3+5;
int mod,Ans;
LL m;
inline int FP(int x,int k)
{
int t=1;
for(; k; k>>=1,x=x*x%mod)
if(k&1) t=t*x%mod;
return t;
}
inline int C_(int n,int m)
{
if(n<m) return 0;
int up=1,down=1;
for(int i=n-m+1; i<=n; ++i) up=up*i%mod;
for(int i=2; i<=m; ++i) down=down*i%mod;
return up*FP(down,mod-2)%mod;
}
int Lucas(LL n,LL m)
{
int ans=1;
for(; m&&ans; n/=mod,m/=mod) ans=ans*C_(n%mod,m%mod)%mod;
return ans;
}
void DFS(int x,LL s)
{
if(s>m) return;
if(!x) {Ans+=Lucas(m,s)/*C[m][s]*/; return;};
for(int i=L[x]; i<=R[x]; ++i) DFS(x-1,s+i);
}
void Main(int n,LL m,int P)
{
TEMP::m=m, mod=P;
Ans=0, DFS(n,0);
printf("%d\n",Ans%P);
}
}/*
2 229 2
49 105
43 152
*/
int main()
{
// freopen("combination.in","r",stdin);
// freopen("combination.out","w",stdout);
// int n=2,m=229,P=2; L[1]=L[2]=0, R[1]=105, R[2]=42;
// return TEMP::Main(n,m,P),0;
const int n=read(); const LL m=read(); const int P=read();
for(int i=1; i<=n; ++i) L[i]=read(),R[i]=read();
if(n<=2 && P==2) return Subtask4::Main(n,m),0;
TEMP::Main(n,m,P);
return 0;
// int n=2, P=2;
// for(int m=2; m<=3000; ++m)
// {
// for(int i=1; i<=n; ++i) R[i]=rand()%233+2, L[i]=std::max(1ll,std::max(rand()%R[i],R[i]-5000));
// printf("m:%d L:%d R:%d %d %d\n",m,L[1],R[1],L[2],R[2]);
// Subtask4::Main(n,m),0;
// TEMP::Main(n,m,P);
// puts("");
// }
return 0;
}
C
另20分的SA写挂了...懒得调.jpg
#include <set>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e4+5,BIT=16,INF=0x7fffffff;
int A[N],mx[BIT][N],Log[N];
char s[N],IN[MAXIN],*SS=IN,*TT=IN;
struct Suffix_Array
{
int sa[N],sa2[N],rk[N],ht[N],tm[N],Log[N],st[16][N];
inline int LCP(int l,int r)
{
if(l>r) std::swap(l,r);
++l; int k=Log[r-l+1];
return std::min(st[k][l],st[k][r-(1<<k)+1]);
}
inline int LCP2(int l,int r)
{
l=rk[l], r=rk[r];
if(l>r) std::swap(l,r);
++l; int k=Log[r-l+1];
return std::min(st[k][l],st[k][r-(1<<k)+1]);
}
void Build(const char *s,const int n)
{
int m=27,*x=rk,*y=sa2;
for(int i=0; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'a'+1];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[i]]--]=i;
for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
{
for(int i=n-k+1; i<=n; ++i) y[++p]=i;
for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
for(int i=0; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
std::swap(x,y), x[sa[1]]=p=1;
for(int i=2; i<=n; ++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if(p>=n) break;
}
for(int i=1; i<=n; ++i) rk[sa[i]]=i;
ht[1]=0;
for(int i=1,k=0; i<=n; ++i)
{
if(rk[i]==1) continue;
if(k) --k;
int p=sa[rk[i]-1];
while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
ht[rk[i]]=k;
}
// for(int i=1; i<=n; ++i) printf("%d:sa:%d ht:%d rk:%d\n",i,sa[i],ht[i],rk[i]);
st[0][1]=ht[1];
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1, st[0][i]=ht[i];
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
st[j][i]=std::min(st[j-1][i],st[j-1][i+t]);
}
}sa;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void Init_ST(int n)
{
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
mx[j][i]=std::max(mx[j-1][i],mx[j-1][i+t]);
}
inline int Query(int l,int r,int x)
{
if(l==r) return INF;
if(l>r) std::swap(l,r);
r+=x-1;
int k=Log[r-l+1];
// printf("Query(%d,%d)=%d\n",l,r,std::max(mx[k][l],mx[k][r-(1<<k)+1]));
return std::max(mx[k][l],mx[k][r-(1<<k)+1]);
}
namespace Subtask1
{
void Solve(const int n)
{
int ans=INF,ok=0,L=read(),R=read(),x=read();
R=R-x+1;
for(int i=L; i<R; ++i)
for(int j=i+1; j<=R; ++j)
if(sa.LCP2(i,j)>=x) ok=1, ans=std::min(ans,Query(i,j,x));
printf("%d\n",ok?ans:-1);
}
void Main(int n,int m)
{
for(int T=1; T<=m; ++T) Solve(n);
}
}
namespace Subtask2
{
#define In(x,l,r) (l<=x&&x<=r)
void Solve(const int n)
{
std::set<int> st;
int L=read(),R=read(),x=read();
R=R-x+1;
if(R<=L) {puts("-1"); return;}
int l=1,ans=INF,ok=0;
// printf("L:%d R:%d x:%d\n",L,R,x);
for(int r=1; r<=n; ++r)
{
while(l<r && sa.LCP(l,r)<x)
{
if(In(sa.sa[l],L,R)) st.erase(sa.sa[l]);
++l;
}
if(!In(sa.sa[r],L,R)) continue;
std::set<int>::iterator it=st.upper_bound(r);
if(it!=st.end()) ok=1, ans=std::min(ans,Query(sa.sa[r],*it,x));
if(it!=st.begin()) --it, ok=1, ans=std::min(ans,Query(sa.sa[r],*it,x));
st.insert(sa.sa[r]);
}
printf("%d\n",ok?ans:-1);
}
void Main(int n,int m)
{
for(int T=1; T<=m; ++T) Solve(n);
}
}
namespace Subtask3
{
void Solve(const int n)
{
std::set<int> st;
int L=read(),R=read(),x=read();
R=R-x+1;
if(R<=L) {puts("-1"); return;}
int l=1;
for(int r=1; r<=n; ++r)
{
while(l<r && sa.LCP(l,r)<x)
{
if(In(sa.sa[l],L,R)) st.erase(sa.sa[l]);
++l;
}
if(!In(sa.sa[r],L,R)) continue;
if(!st.empty()) {printf("%d\n",A[1]); return;}
st.insert(sa.sa[r]);
}
puts("-1");
}
void Main(int n,int m)
{
for(int T=1; T<=m; ++T) Solve(n);
}
}
int main()
{
// freopen("string.in","r",stdin);
// freopen("string.out","w",stdout);
const int n=read(),m=read();
scanf("%s",s+1);
for(int i=1; i<=n; ++i) mx[0][i]=A[i]=read();
Init_ST(n), sa.Build(s,n);
if(n<=100) return Subtask1::Main(n,m),0;
if(n<=1000) return Subtask2::Main(n,m),0;
Subtask3::Main(n,m);
return 0;
}
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------