計蒜客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<bits/stdc++.h>
#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<bits/stdc++.h>
#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<typename T> 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<bits/stdc++.h>
#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<typename T> 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<typename T> void qwq(T x){
	if(x>9)qwq(x/10);
	putchar(x%10+'0');
}
template<typename T> 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<<i);}
		}
		write(ans+1);
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章