[BZOJ2227][Zjoi2011][找规律][排列组合][数学]看电影(movie)

首先数据很小……可以打表……找规律

找出来是 (k+1)n1(kn+1)kn

证明可以看这篇

我的约分的方法比较奇怪,因为只有200以下的因子,就暴力枚举1~200,分子分母能同时除就除掉,当初是从Manchery那看到的,感觉打起来要方便一点……

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>

using namespace std;

int n,k,t;
int p[210],c[210];

struct Int{
    int a[1010],sz;
    #define base 100000
    Int(int x=0){ 
        memset(a,0,sizeof(a)); sz=0;
        while(x)a[++sz]=x%base,x/=base; 
    }
    int &operator [](int x){ return a[x]; }
    inline bool empty(){
        return sz==1&&a[1]==0;
    }
    friend Int operator +(Int A,Int B){
        Int C; C.sz=A.sz+B.sz;
        if(B.empty()) return A;
        if(A.empty()) return B;
        for(int i=1;i<=C.sz;i++) C[i]=A[i]+B[i];
        for(int i=1;i<=C.sz;i++)
            if(C[i]>=base){C[i+1]+=C[i]/base,C[i]%=base; if(i==C.sz)C.sz++;}
        return C;
    }
    friend Int operator *(Int A,Int B){
        Int C; C.sz=A.sz+B.sz-1;
        if(B.empty()) return B;
        for(int i=1;i<=A.sz;i++)
            for(int j=1;j<=B.sz;j++)
            C[i+j-1]+=A[i]*B[j];
        for(int i=1;i<=C.sz;i++)
            if(C[i]>=base){C[i+1]+=C[i]/base,C[i]%=base; if(i==C.sz)C.sz++;}
        return C;
    }
    friend Int operator -(Int A,Int B){
        for(int i=1;i<=B.sz;i++) A[i]-=B[i];
        for(int i=1;i<=A.sz;i++)
            if(A[i]<0) A[i]+=base,A[i+1]--;
        while(!A[A.sz]&&A.sz) A.sz--;
        if(!A.sz) A.sz++;
        return A;
    }
    friend Int operator /(Int A,int B){
        int x=0;
        for(int i=A.sz;i;i--){
            int t=(A[i]+x*base)%B;
            A[i]=(A[i]+x*base)/B;
            x=t;
        }
        while(!A[A.sz]&&A.sz) A.sz--;
        return A;
    }
    friend Int operator %(Int A,int B){
        return A-(A/B)*B;
    }
    Int &operator *=(int B){ *this=*this*B; return *this; }
    Int &operator /=(int B){ *this=*this/B; return *this; }
    inline void print(){
        if(!sz) putchar(48);
        printf("%d",a[sz]);for(int i=sz-1;i;i--) printf("%05d",a[i]);
    }
};

inline void simplify(Int &A,Int &B){
    for(int i=2;i<=200;i++)
        while((A%i).empty()&&(B%i).empty()) A/=i,B/=i;
}

Int Ans,Ans1;

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&k);
        if(k<n) { puts("0 1"); continue; }
        Ans=1; Ans1=1;
        for(int i=1;i<=n-1;i++) Ans*=k+1;
        for(int i=1;i<=n;i++) Ans1*=k;
        Ans*=k-n+1;
        simplify(Ans,Ans1);
        Ans.print(); putchar(' '); Ans1.print(); putchar('\n');
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值