2015.08.08总结

NOIP2015提高组模拟8.8

P1 3476. 【NOIP2013初赛】整除

这题其实是一道货真价实的水题,正解就是DFS+容斥原理。先DFS枚举若干个数的Lcm再根据容斥原理计算,可得出答案。顺便,可以将题目转化为求[1~R]-[1~L-1],便于处理。

P2 3473. 铺砖问题

嘛。。。经典问题,就个人感觉来说,这道题是这套题中最难的一道。正解是矩阵优化+快速幂。50分的状压DP(设F[i][j]表示做完前i列),可以预处理出转移的状态,便于后面的转移。另外的50分用矩阵乘法+快速幂来处理,还是先DFS预处理一个矩阵Mat,Mat[i][j]=1表示压缩后的状态可以转移到j,再求该矩阵的n次幂,最后答案就是Mat[0][0].
SRC:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 100+10
#define M 2500+10
typedef long long ll;
const int Mo=1000000007;
struct Matrixtype {
    int mat[N][N];
}a,b,tmp,ma;

int n[210];
int jl[M][M],f[2][M];
char s[210];
int m,maxn,ans;

void Pre(int st,int k,int num) {
    if(k>=m) {
        jl[st][++jl[st][0]]=num;
        return;
    }
    if((1 << k) & st) {Pre(st,k+1,num); return;}
    Pre(st,k+1,num+(1 << k));
    if(k+1<m && !((1 << (k+1)) & st)) Pre(st,k+2,num);
}

void Make(int st,int k,int num) {
    if(k>=m) {
        ma.mat[st][num]=1;
        return;
    }
    if((1 << k) & st) {Make(st,k+1,num); return;}
    Make(st,k+1,num+(1 << k));
    if(k+1<m && !((1 << (k+1)) & st)) Make(st,k+2,num);
}

int Div() {
    int r=0;
    for(int i=n[0];i>0;i--) {
        n[i]+=r*10;
        r=n[i]%2;
        n[i]/=2;
    }
    if(!n[n[0]]) n[0]--;
    return r;
}

Matrixtype mult(Matrixtype a,Matrixtype b) {
    Matrixtype c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0;i<=maxn;i++) {
        for(int j=0;j<=maxn;j++) {
            for(int k=0;k<=maxn;k++) {
                c.mat[i][j]=(c.mat[i][j]+(ll)a.mat[i][k]*b.mat[k][j])%Mo;
            }
        }
    }
    return c;
}

void Quickmi() {
    for(int i=0;i<=maxn;i++) tmp.mat[i][i]=1;
    while(n[0]) {
        int rest=Div();
        if(rest) tmp=mult(tmp,ma);
        ma=mult(ma,ma);
    }
}

int main()
{
    freopen("p2.in","r",stdin);
    freopen("p2.out","w",stdout);
    scanf("%s%d",s,&m);
    int len=strlen(s);
    maxn=(1 << m) - 1 ;
    if(m>5) {
        f[0][0]=1;
        int nn=0,now=0;
        for(int i=0;i<len;i++) nn=nn*10+s[i]-'0';
        for(int i=0;i<=maxn;i++) Pre(i,0,0);
        for(int i=0;i<nn;i++) {
            for(int j=0;j<=maxn;j++) {
                if(!f[now][j]) continue;
                for(int k=1;k<=jl[j][0];k++) {
                    f[~now][jl[j][k]]=(f[~now][jl[j][k]]+f[now][j])%Mo;
                }
            }
            memset(f[now],0,sizeof(f[now]));
            now=~now;
        }
        ans=f[now][0];
    } else {
        n[0]=len;
        for(int i=0;i<len;i++)
        {
            if(i==len-1) {
                i++;
                i--;
                i++;
                i--;
            }
            n[len-i]=s[i]-'0';
        }
        for(int i=0;i<=maxn;i++)
            Make(i,0,0);
        Quickmi();
        ans=tmp.mat[0][0];
    }
    printf("%d\n",ans);
}

P3 3475. 【NOIP2012初赛】新壳栈

初赛原题,这题可以维护一个双向队列和一个栈,对于栈顶的c个元素,我们放入队列中,其余元素放在栈中。对于翻转操作只需要O(1)交换队头和队尾指针即可。但不知为什么,用C++的reverse()跑的特别快,因此诞生了一种水法。
水法:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 1000000+10

int q[N];
int c,k,top;

int main()
{
    scanf("%d",&c);
    scanf("%d",&k);
    while(k!=0) {
        if(k==1) {
            int e;
            scanf("%d",&e);
            q[++top]=e;
        }
        if(k==2) {
            if(top<1) printf("Error: the stack is empty!\n");
            else printf("%d\n",q[top]),top--;
        }
        if(k==3) {
            if(top<c) printf("Error: less than %d elements in the stack!\n",c);
            else reverse(q+1+top-c,q+1+top);
        }
        scanf("%d",&k);
    }
    return 0;
}

P4 【NOIP2013初赛】青蛙

我们设F[i]表示n=i的答案。易得F[i]=(F[1]+F[2]+…+F[i])/n+1
化简得F[i]=(F[1]+F[2]+…+F[i-1]+i)/(n-1)

SRC:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 20000+10

double f[N],s[N];
int n;

int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++) f[i]=(s[i-1]+i)/(i-1),s[i]=f[i]+s[i-1];
    printf("%.2f\n",f[n]);
    return 0;
}

以上.

你可能感兴趣的:(状压dp,矩阵乘法优化)