计蒜客2019年12 月 CSP-S2 模拟赛题解

A.课后练习题

Solution:

        注意到有x^{1}\equiv x(mod (x+1)),x^{2}\equiv x^{2}+2x+2\equiv (x+1)^{2}+1\equiv 1(mod(x+1)).

        所以对于偶数k,满足n-1\equiv 0(mod(x+1))

               对于奇数k,满足n+1\equiv 0(mod(x+1))

        所以答案即为n-1/n+1的因子个数(除1外)

Code:

#include
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
int main(){
	long long n,k;scanf("%lld%lld",&n,&k);
    n+=(k%2==1);n-=(k%2==0);
    int ans=-1;
    for(long long i=1;i*i<=n;i++){
        if(n%i!=0)continue;
        ans++;ans+=(i*i!=n);
    }
    printf("%lld\n",ans);
    return 0;
}

B.拼图

Solution:

        首先暴力搜出N从0到8的答案,

        接着列出转移方程dp[n]=dp[n-1]+dp[n-2]+5dp[n-4]-dp[n-5]+dp[n-6]-dp[n-8]

        (详细证明)

        由于N过大,考虑矩阵乘法,笔者的设计如下(似乎冗余了一行啊。。。)

        \begin{bmatrix} dp0&dp1& dp2& dp3 & dp4 & dp5 & dp6 & dp7 &dp8 \end{bmatrix}

                            *(我是乘号)

        \begin{bmatrix} 0& 0 & 0 & 0&0 & 0 &0 & 0& 0\\ 1& 0& 0 & 0& 0 & 0& 0&0 &-1 \\ 0 & 1 & 0& 0 &0 & 0& 0 & 0& 0\\ 0& 0& 1& 0& 0& 0 & 0& 0&1 \\ 0& 0& 0 & 1& 0 & 0& 0 & 0 &-1 \\ 0& 0 &0 & 0 &1 & 0 & 0 &0 & 5\\ 0& 0 &0 & 0 & 0 &1& 0& 0&0 \\ 0 & 0& 0 & 0 & 0 &0 & 1 & 0& 1\\ 0 & 0 & 0& 0& 0 & 0& 0& 1& 1 \end{bmatrix}

                          =(我是等号)

        \begin{bmatrix} dp1 & dp2 & dp3 & dp4 & dp5 &dp6 &dp7 &dp8 &dp9 \end{bmatrix}

        PS:注意运算过程中由于需要取模,矩阵中的dp[n]可能为负数,输出是要注意加上模数。

Code:

#include
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define mo 1000000007
using namespace std;
int a[10][10];int b[10][10];int t[10][10];
template void chkmod(T &x,T y){x=(x+y)%mo;}
inline void scz(int q[][10],int w[][10]){
    rep(i,1,9){
        rep(j,1,9){
        	t[i][j]=0;
            rep(k,1,9){
				int nop=(1ll*q[i][k]*w[k][j])%mo;chkmod(t[i][j],nop);
            }
        }
    }
	rep(i,1,9){
        rep(j,1,9)q[i][j]=t[i][j];
    }
    return;
}
int main(){
    long long n;scanf("%lld",&n);
    a[1][1]=a[1][2]=1;a[1][3]=2;a[1][4]=3;a[1][5]=9;a[1][6]=16;
    a[1][7]=35;a[1][8]=65;a[1][9]=143;
    if(n<9){printf("%d\n",a[1][n+1]);return 0;}
    
    rep(i,1,8)b[i+1][i]=1;
    b[2][9]=-1;b[4][9]=1;b[5][9]=-1;b[6][9]=5;b[8][9]=1;b[9][9]=1;
    long long tmp=n-8;
    while(tmp){
        if(tmp&1)scz(a,b);
		scz(b,b);
		tmp>>=1;
    }
    printf("%d\n",(a[1][9]+mo)%mo);
    return 0;
}

C.魔法

Solution:

        我们很自然地考虑如果单独拎出一整个序列怎么回答询问,将该序列划分成若干段正好包含1-d的,

        如\left [ 4,2,3,3,1,4,2,2,1,3,3\right ]分成\left [ 4,2,3,3,1\right ]\left [ 4,2,2,1,3 \right ]两组(多余的1个3忽略)。

        设分出的段数为M,则显然,长度<=M的魔法都会被其包含。

        其次,构造一个长度为M+1的不包含的魔法:选取每一段的最后一个数字,再选一个剩余部分中没有的数字即可。

        接下来想办法处理多组询问:

        首先可以用2-pointers处理出从i点开始划分,到哪里可正好分为一段,记作nxt[i]。

        如果一个i的nxt[i]存在的话,我们可以从nxt[i]+1向i连一条边,这样我们可以得到一个森林,

        那每一次询问l,r可以看成求l的祖先中,编号<=r的深度最小的祖先到l的距离+1。

        询问和预处理参考倍增LCA即可。

Code:

#include
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rep2(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
template void read(T &num){
	char c=getchar();T f=1;num=0;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
	num*=f;
}
template void qwq(T x){
	if(x>9)qwq(x/10);
	putchar(x%10+'0');
}
template void write(T x){
	if(x<0){x=-x;putchar('-');}
	qwq(x);putchar('\n');
}
int co[500010];int nxt[500010];int t[1010];
struct wzy{
	int nxt,vertice;
}edge[500010];
int head[500010];int len=0;
inline void add_edge(int x,int y){
	edge[++len].nxt=head[x];edge[len].vertice=y;head[x]=len;return;
}
int f[500010][19];
inline void Pretreatment(int u,int fa){
	f[u][0]=fa;
	rep(i,1,18)f[u][i]=f[f[u][i-1]][i-1];
	for(int i=head[u];i;i=edge[i].nxt){
		int nop=edge[i].vertice;
		Pretreatment(nop,u);
	}
	return;
}

int main(){
	//freopen("magic2.in","r",stdin);
	//freopen("magic.out","w",stdout);
	int n,m,d;read(n);read(m);read(d);
	rep(i,1,n)read(co[i]);
	
	int r=1;t[co[1]]++;int cnt=1;
	rep(i,1,n){
		while(cnt!=d&&r!=n){r++;cnt+=(t[co[r]]==0);t[co[r]]++;}
		if(r==n&&cnt!=d)break;
		nxt[i]=r;cnt-=(t[co[i]]==1);t[co[i]]--;
	}
	rep(i,1,n+1){
		if(!nxt[i]){Pretreatment(i,0);}
		else{add_edge(nxt[i]+1,i);}
	}
	
	while(m--){
		int l,r;read(l);read(r);
		int ans=0;
		rep2(i,18,0){
			if(f[l][i]&&f[l][i]<=r+1){l=f[l][i];ans+=(1<

 

你可能感兴趣的:(矩阵乘法,比赛题解,树上DP)