7.10模拟赛

 (题目在文末)

T1.qiang

不写逆元见祖宗..

首先一个合法方案最少要包含一个形如1,2,3,..n的数列,再考虑在其中插入一些数。

考虑以x结尾的一种方案,x贡献了了num[x]次,在初始序列中出现了一次,所以贡献了num[x]-1次,其中的每一种,都有sum[x]-1个地方可以放(至少放在x前,不然就违背了定义)

所以答案就是π(C(sum[i]-1,num[i]-1))(之前都想得很正常,接下来开始表演爆0)

具体计算组合数时,显然要预处理阶乘,然而这里有乘法,所以要用逆元(这不是显然吗,我的脑子呢..)

#include<iostream>
#include<cstdio>

using namespace std;

const int MAXN=500005;
const int N=500000;

const int MOD=998244353;

typedef long long ll;

inline ll rd(){
    ll ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}



ll po(ll x,ll y) {
    ll ret=1,base=x;
    while(y) {
        if(y&1) (ret*=base)%=MOD;
        (base*=base)%=MOD;
        y>>=1;
    }
    return ret;
}

ll fac[MAXN],inv[MAXN];

void prework() {
    fac[0]=1;
    for(int i=1; i<=N; i++) fac[i]=(fac[i-1]*i)%MOD;
    inv[N]=po(fac[N],MOD-2);
    for(int i=N-1; i>=0; i--) inv[i]=(inv[i+1]*(i+1))%MOD;
}

inline ll C(ll x,ll y){
    if(y>x) return 0;
    return (1ll*(fac[x]*inv[y])%MOD*inv[x-y])%MOD;
}

ll ans=1;
ll n;
ll num[MAXN],sum[MAXN];

int main() {
    prework();
    n=rd();
    for(int i=1;i<=n;i++) num[i]=rd(),sum[i]=sum[i-1]+num[i];
    for(int i=1;i<=n;i++){
        ans*=C(sum[i]-1,num[i]-1);
        ans%=MOD;
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 T2.count

30分:朴素状压 O(4*T*(2^n))

60分:行列交换之后状压,复杂度O(T*n*256) (数据水,其实这里就能拿100分了)

100分:对于状压的两个可行状态i和j,且i可以向j转移,那么在i和j之间连边,把原问题转化为在图上走n步到达制定状态的问题。

邻接矩阵存图,矩阵乘法。

或者是归纳出一个神奇的公式f(n)=f(n-1)+5*f(n-2)+f(n-3)-f(n-4)

60分做法:

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

int f[100005][18];

const int UP=(1<<4)-1;

bool legal[32];
int n,m;

void prework(){
    for(int i=0;i<=UP;i++){
        int cnt=0,succ=0;
        for(int j=0;j<4;j++){
            if(i&(1<<j)) {if(cnt&1) break;cnt=0;}
            else cnt++;
        }
        succ=1;
        if(cnt&1) continue;
        legal[i]=succ;
    }
}

int main(){
    prework();
    int T;
    while(cin>>n>>m){
        if(!n) return 0;
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=UP;j++){
                if(!legal[j]) continue;
                for(int k=0;k<=UP;k++){
                    if(!legal[k|j]) continue;
                    if(j&k) continue;
                    f[i][j]+=f[i-1][k];
                    f[i][j]%=m;
                }
            }
        }
        cout<<f[n][0]<<endl;
    }
    return 0;
}
    
View Code

100分做法:

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

const int MAXN=32;
const int UP=16;
int MOD;

struct Mat {
    int data[MAXN][MAXN];
    Mat() {memset(data,0,sizeof(data));}
    Mat operator*(const Mat &rhs) {
        Mat ret;
        for(int k=0; k<UP; k++)
            for(int i=0; i<UP; i++)
                for(int j=0; j<UP; j++)
                    ret.data[i][j]+=data[i][k]*rhs.data[k][j],//
                        ret.data[i][j]%=MOD;
        return ret;
    }
};
Mat pow(Mat y,int x) {
    Mat ret,base=y;
    for(int i=0; i<UP; i++) ret.data[i][i]=1;
    while(x) {
        if(x&1) ret=ret*base;
        base=base*base;x>>=1;
    }
    return ret;
}

int legal[MAXN];

void prework() {
    for(int i=0; i<=UP; i++) {
        int cnt=0,succ=0;
        for(int j=0; j<4; j++) {
            if(i&(1<<j)) {
                if(cnt&1) break;
                cnt=0;
            } else cnt++;
        }
        succ=1;
        if(cnt&1) continue;
        legal[i]=succ;
    }
}

Mat sav,ori;
int n;

int main() {
    prework();
    for(int i=0; i<UP; i++) {
        for(int j=0; j<UP; j++) {
            if(!legal[i|j]) continue;
            if(i&j) continue;
            ori.data[i][j]=1;
        }
    }
    while(cin>>n>>MOD) {
        if(!n) break;
        Mat tmp=pow(ori,n-1);
        int ans=0;
        for(int i=0; i<UP; i++) if(legal[i]) ans+=tmp.data[0][i];
        cout<<ans%MOD<<endl;
    }
    return 0;
}
View Code

 T3.sequence

理解是这样的,先考虑一个弱化版本,如果不是mod 4意义下呢?

假设B[i]>A[i],那么每个A[i]需要增加C[i]=B[i]-A[i],不妨处理出这个C[i]

比如,我们有A[]={0,3,1,2,1},B[]={3,7,8,6,8},那么可以算出C[]={3,4,7,4,7},不妨看成一个柱状图

区间整体加1,可以看成横向削一层,不难发现答案就是Σmax(0,a[i]-a[i-1])

这启发我们在考虑mod 4意义下的问题时,也另外考虑一个差分数组d(diff)[i]

对原区间进行区间加,就是差分数组的单点修改,考虑一个区间[i,j],我们要做的就是d[i]++,d[j]--

现在就来考虑一下如何贪心地给d[j]寻找一个对应的d[i],使得配对代价最小。

用一个桶维护前面出现过的数,并只计算正数的贡献(max(0,x)),贪心地从小往大选

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

const int MAXN=1e7+10;

int a[MAXN],b[MAXN],c[MAXN],d[MAXN],s[MAXN];
int n;

int main(){
    int T;
    cin>>T;
    while(T--){
        cin>>n;
        memset(s,0,sizeof(s));
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>b[i];
        for(int i=1;i<=n;i++) c[i]=(b[i]-a[i]+4)%4;
        for(int i=1;i<=n;i++) d[i]=c[i]-c[i-1];
        int ans=c[1],j;
        for(int i=2;i<=n;i++){
            s[(d[i]+3)%4+1]++;
            if(d[i]<=0) continue;
            for(j=0;!s[j];j++);
            s[j]--;
            ans+=j;
        }
        cout<<ans<<endl;
    }
    return 0;
}
View Code

官方题解


本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9289735.html

原文地址:https://www.cnblogs.com/ghostcai/p/9289735.html