191106CSP(NOI?)模拟及NOI(CSP?)模拟题解

CSP模拟:
T1:求 ∑ i = 1 n ∑ j = 1 m C g c d ( i , j ) B % m o d \sum_{i=1}^n\sum_{j=1}^mC_{gcd(i,j)}^B \%mod i=1nj=1mCgcd(i,j)B%mod
n , m ≤ 1 e 10 , B ≤ m o d = 9990017 n,m\le1e10,B\le mod=9990017 n,m1e10,Bmod=9990017
莫比乌斯反演,设 f ( i ) f(i) f(i)表示 C i B C_i^B CiB
∑ i = 1 n ∑ j = 1 m f ( g c d ( i , j ) ) \sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)) i=1nj=1mf(gcd(i,j))
∑ d = 1 m i n ( n , m ) d ∑ i = 1 ⌊ n d ⌊ ∑ j = 1 ⌊ m d ⌊ f ( d ) [ g c d ( i , j ) = = 1 ] \sum_{d=1}^{min(n,m)}d\sum_{i=1}^{\lfloor \frac{n}{d} \lfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \lfloor}f(d)[gcd(i,j)==1] d=1min(n,m)di=1dnj=1dmf(d)[gcd(i,j)==1]
∑ d = 1 m i n ( n , m ) d ∑ x = 1 f ( d ) μ ( x ) ⌊ n x d ⌋ ⌊ m x d ⌋ \sum_{d=1}^{min(n,m)}d\sum_{x=1}f(d)\mu(x)\lfloor \frac{n}{xd} \rfloor \lfloor \frac{m}{xd} \rfloor d=1min(n,m)dx=1f(d)μ(x)xdnxdm
∑ T = 1 m i n ( n , m ) ( ∑ d ∣ T f ( T d ) μ ( d ) ) ⌊ n T ⌋ ⌊ m T ⌋ \sum_{T=1}^{min(n,m)}(\sum_{d|T}f(\frac{T}{d})\mu(d))\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor T=1min(n,m)(dTf(dT)μ(d))TnTm

杜教筛中间的,然后整除分块
中间的卷上一个 I I I就完事,还要用到一个定理: ∑ i = 1 n ( B i ) \sum_{i=1}^n(_B^i) i=1n(Bi)= ( B + 1 i + 1 ) (_{B+1}^{i+1}) (B+1i+1)

Code:

#include
#define ll long long
#define mod 9990017
using namespace std;
inline ll read(){
	ll res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=1ll*x*y%mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=3e6+5;
int pri[N],pt[N],tot=0,mu[N],f[N];
int fac[mod],ifac[mod];
int B;
inline int C(int n,int m){if(n<0 || m<0 || n<m) return 0;return mul(fac[n],mul(ifac[m],ifac[n-m]));}
inline int lucas(int n,int m){
	if(n<mod && m<mod) return C(n,m);
	return mul(lucas(n/mod,m/mod),C(n%mod,m%mod));
}
inline void init(int n){
	fac[0]=ifac[0]=1;
	for(int i=1;i<mod;i++) fac[i]=mul(fac[i-1],i);
	ifac[mod-1]=ksm(fac[mod-1],mod-2);
	for(int i=mod-2;i;i--) ifac[i]=mul(ifac[i+1],i+1);
	mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!pt[i]) pri[++tot]=i,mu[i]=dec(0,1);
		for(int j=1;j<=tot && i*pri[j]<=n;j++){
			pt[i*pri[j]]=1;
			if(i%pri[j]==0) break;
			mu[i*pri[j]]=dec(0,mu[i]);
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1,tmp=lucas(i,B);i*j<=n;j++) inc(f[i*j],mul(tmp,mu[j]));
	for(int i=1;i<=n;i++) inc(f[i],f[i-1]);
}
map<ll,int>mp;
int S(ll x){
	if(x<=3000000) return f[x];
	if(mp.count(x)) return mp[x];
	int res=lucas(x+1,B+1);
	for(ll i=2,j;i<=x;i=j+1){
		j=x/(x/i);
		Dec(res,mul(dec(add(j,1),i),S(x/i)));
	}
	return mp[x]=res;
}
int main(){
	ll n=read(),m=read();B=read();
	init(3000000);
	int ans=0,now=0,last=0;
	for(ll i=1,j;i<=min(n,m);i=j+1){
		j=min(n/(n/i),m/(m/i));now=S(j);
		inc(ans,mul(n/i%mod,mul(m/i%mod,dec(now,last))));
		last=now;
	}
	cout<<ans;
	return 0;
}

T2:一个 2 ∗ n 2*n 2n的点集,一共有 n n n条弦,每条弦由两个点组成,每个点仅属于一条弦,每次询问给出两个点 r 1 , r 2 r1,r2 r1,r2,询问有多少个 l l l满足区间 [ l , r 1 ] [l,r1] [l,r1] [ l , r 2 ] [l,r2] [l,r2]都不会跨过任意一条弦的恰好一个点, n ≤ 2 e 6 n\le2e6 n2e6

单调栈预处理每个点往左的极小合法区间左端点,然后每个点向其极小合法区间左端点连边,形成一棵树,则不难发现答案就是两点间 l c a lca lca的深度

Code:

#include
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2e6+5;
int vis[N],head[N],nxt[N],tot=0;
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
int siz[N],hson[N],fa[N],dep[N],rt[N];
int R;
void dfs1(int v){
	siz[v]=1;rt[v]=R;
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(y==fa[v]) continue;
		fa[y]=v;dep[y]=dep[v]+1;dfs1(y);
		siz[v]+=siz[y];
		if(siz[y]>siz[hson[v]]) hson[v]=y;
	}
}
int top[N];
void dfs2(int v,int T){
	top[v]=T;
	for(int i=head[v];i;i=nxt[i])
		if(top[vis[i]]==-1) dfs2(vis[i],vis[i]==hson[v]?T:vis[i]); 
}
inline int lca(int x,int y){
	while(top[x]!=top[y])
		dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
	int lc=dep[x]<dep[y]?x:y;
	return dep[lc];
}
int sta[N],tp=0;
int l[N],r[N];
int a[N];
int main(){
	#ifdef romiqi
	freopen("hotchkiss1.in","r",stdin);
	freopen("lx.out","w",stdout);
	#endif
	int n=read()*2,q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++){
		l[i]=min(i,a[i]);
		r[i]=max(i,a[i]);
		while(tp && sta[tp]>=l[i]){
			l[i]=min(l[i],l[sta[tp]]);
			r[i]=max(r[i],r[sta[tp]]);
			--tp;
		}
		sta[++tp]=i;
	}
	memset(rt,-1,sizeof(rt));
	memset(top,-1,sizeof(top));
	for(int i=1;i<=n;i++) if(i==r[i]) add(l[i]-1,i); 
	for(int i=0;i<=n;i++) if(rt[i]==-1){R=i;dfs1(i);dfs2(i,i);}
	while(q--){
		int x=read(),y=read();
		if(x<1 || y<1 || x>n || y>n || rt[x]!=rt[y]) puts("0");
		else cout<<lca(x,y)<<"\n";
	}
	return 0;
}

T3:多次给出一个 4 ∗ n 4*n 4n的矩阵,求用俄罗斯方块将其恰好填满的方案数 n ≤ 1 e 9 , T ≤ 1000 n\le1e9,T\le1000 n1e9,T1000
显然是状态压缩后用矩阵快速幂优化,然而其有常系数线性递推式,所以可以BM打表+多项式取模

Code:咕咕咕

NOI模拟:
T1:CSPT3
T2:一个sb计数,懒得写了
T3:一个sb推式子,懒得写了

你可能感兴趣的:(杜教筛,莫比乌斯反演,BM)