中国石油大学ACM俱乐部开放训练赛

问题 A: sciorz画画

题目描述

众所周知,sciorz会画画。某天,sciorz画了一个凸多边形,这个多边形的每个顶点都有一个权值a[i]。sciorz觉得这个凸多边形不够美丽,于是他决定在n个点之间连线,最终用n-3条不相交的线将这个凸n边形分割成n-2个三角形。sciorz认为,一个三角形的美丽值是三个顶点权值的乘积,凸多边形的美丽值是其内部三角形的美丽值的和。sciorz想找到一种分割方案,使得这个凸多边形的美丽值最大。sciorz忙着刷难题,所以他随手就把这个签到题扔给你,希望你帮sciorz算出最大的美丽值。

输入

第一行一个t,表示有t组样例。
每组样例的第一行是一个n,表示多边形的边数。
第二行n个数,第i个数表示多边形第i个顶点的权值a[i],按逆时针顺序给出。

输出

对于每组样例,输出一行。格式为"Case #x: y",x为样例编号,y为答案。

样例输入 Copy

2
3
1 2 3 
4
1 2 3 4

样例输出 Copy

Case #1: 6
Case #2: 32

提示

第一个样例只有一个三角形,所以不用分割,答案是1*2*3=6。
第二个三角形,最优分割方案是分割为1 2 4和2 3 4两个三角形,答案是1*2*4+2*3*4=32

1<=t<=100
3<=n<=100
1<=a[i]<=100
 
一开始忘了输出格式,莫名wa了两发,剁手
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
int n;
ll a[1000];
ll dp[1000][1000];
int ca=1;
void solve() {
    memset(a,0,sizeof 0);
    memset(dp,0,sizeof(dp));
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%lld",&a[i]);
    for(int i=n; i>=1; i--) {
        for(int j=i+1; j<=n; j++) {
            if(j-i==1)
                dp[i][j]=0;
            else if(j-i==2)
                dp[i][j]=a[i]*a[i+1]*a[i+2];
            else
                for(int k=i+1; k<=j-1; k++) {
                    dp[i][j]=max(dp[i][j],(dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]));
                }
        }
    }
    printf("Case #%d: ",ca++);
    printf("%lld
",dp[1][n]);
}
int main() {
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

问题 B: 奎奎发红包

题目描述

情人节又到了,又到了一年一度发红包的时间。经大家研究决定,今年让奎奎自愿发红包。
俱乐部有n个人(0<n<100000),每个人都有一个单身值v[i]与亲密度t[i](0≤v[i]≤10000,0≤t[i]≤10000),单身值越大的人,在情人节的时候就越羡慕奎奎,奎奎就需要给他更大的红包来安慰他。 由于一个寒假没有见到奎奎,领红包的时候大家都想跟奎奎py,花费时间t[i],先py后给红包噢。
大家都厌倦了等待,如果一个人等了时间t,那么奎奎发给他的红包大小就是v[i]·t。
但是奎奎还要和女朋友去快乐,想要花最少的钱来满足大家。
请你帮他计算最少需要发多少钱的红包。

输入

第一行一个整数n。接下来n行,每行两个数v[i]和t[i]。

输出

一个整数表示答案。

样例输入 Copy

4
1 4
2 3
3 2
4 1

样例输出 Copy

35

//直接贪心
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std ;
const int N=100010;
typedef long long ll;
int f[N];
struct S {
    int v,t;
} st[N];
bool cmp(S a,S b) {
    return a.t*b.v<a.v*b.t;
}
int main() {
    int t=1;
    for(int c=1; c<=t; c++) {
        int n;
        cin>>n;
        for(int i=0; i<n; i++) {
            int s,e;
            cin>>s>>e;
            st[i]= {s,e};
        }
        sort(st,st+n,cmp);
        ll ans=0;
        ll sum=0;
        for(int i=0; i<n; i++) {
            ans+=st[i].t;
            sum+=st[i].v*ans;
        }
        cout<<sum<<endl;
    }
}

问题 C: 关于我转生变成史莱姆这档事

题目描述

关于我转生变成史莱姆这档事这部番剧中,上班族的三上悟因为某个事件而作为史莱姆在异世界转生了。在转生时得到了“大贤者”和“捕食者”这两个独特技能。虽然身为史莱姆,但也想和其他种族建立起友好关系。魔素是异世界里面魔物含有的魔力精华,捕食者这个技能就是吞噬魔素,捕食者的技能要求非常苛刻,如果你第一天吞噬了b魔素,那么你第二天可以吞噬第一天的2~9倍(必须是其中一个整数),也就是2b~9b,也就是说,史莱姆在第i天所吞噬的魔素一定是第i-1天的2~9倍,而且还必须是它的整数倍。
作为史莱姆手下的得力助手,哥布林们准备了大量的魔物供主人食用,现在史莱姆已经知道了这些魔物含有S魔素,现在请大贤者合理安排第一天要吞噬和接下来每天需要增加的魔素倍数,好让史莱姆能在最短的天数内恰好吞噬完魔素。由于大贤者要研究“哲学”,无暇顾及这些小事,现在只能请你帮忙,但是大贤者还建议,这些魔素至少要用两天来吞噬。
 

输入

一个正整数S,代表要吞噬的魔素总量。

输出

一个数,代表要吞噬的天数,如果无解输出-1。

样例输入 Copy

571

样例输出 Copy

5

提示

对于30%数据,有S<=100;
对于70%数据,有S<=107;
对于100%数据,有9<S<=8×108
 
玄学

每一步都要整除,a1表示第一天的量 剩下的表示倍数,能过也是玄学

#include<iostream>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f ;
int read()
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')             //判断正负
        flag=1;
    else if(ch>='0'&&ch<='9')           //得到完整的数
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}
ll ans=INF;
ll calc(ll n)
{
    if(n<2)return INF;
    if(2<=n&&n<=9)return 1;
    ll res=INF;
    for(ll i=2;i<=9;++i)
        if(n%i==0)
        res=min(res,calc(n/i-1)+1);
    return res;
}
int main()
{
    ll n=read();
    for(ll i=1;i*i<=n;++i)
        //枚举因数,也就是第一次取多少 
        if(n%i==0)
        {
            ans=min(ans,calc(n/i-1)+1);
            //对两个因数递归 
            if(i!=1)
                ans=min(ans,calc(i-1)+1);
        }
    if(ans==INF)
        puts("-1");
    else 
        cout<<ans;
    return 0;
}

问题 D: 大数

题目描述

小七是一个很可爱很努力的女孩子。她对大数的运算非常感兴趣,在学习了几天之后,终于精通了大数的加减乘除。但是自从她学会了 JAVA ,她觉得大数实在是太简单太无聊了,因为运用 JAVA 中 BigInteger 大整数类,可以轻松实现大数的加减乘除。某一天她突然发现,很多大数的题目的数据都有规律。这些数都是由比他小的某个数重复构成,比如说 121212 由 12重复构成 ,233233 ,由233重复构成,而1231231并不是由123重复构成。无聊的小七终于找到了乐子,她想要找到每个大数是由哪个比它小的数重复构成。算法刚入⻔的小七无法解决这个问题,于是她求助于你。
 

输入

一行 一个正整数n(n<=101000000)。

输出

一行
如果存在多种,输出最小的满足题意的数;
如果不存在,则输出-1.
 

样例输入 Copy

121212

样例输出 Copy

12

KMP求循环节
#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l; i <= r; ++ i)

using namespace std;

const int MaxN = 1e7 + 10;

int n;
char s1[MaxN];
int p[MaxN];

int main() {
    scanf("%s", s1 + 1);
    n = strlen(s1 + 1);
    if (n == 0) return 0;
    int j = 0;
    rep (i, 1, (n - 1)) {
        while (j > 0 && s1[j + 1] != s1[i + 1])
            j = p[j];
        if (s1[j + 1] == s1[i + 1])
            j ++;
        p[i + 1] = j;
    }
    if (n % (n - p[n]) != 0 || n == (n - p[n])) printf("-1");
    else rep (i, 1, n - p[n]) cout << s1[i];
    return 0;
}

问题 E: Ktree

题目描述

炼金术师灵岩在与巫师Slanely争夺馅饼的过程中处于劣势,于是灵岩召唤出了K树一起争夺馅饼。K树是一棵权值和为s但权值分配方式未定的树。Slanely偷偷施展法术,使K树的直径尽可能小。现在灵岩想知道召唤出的K树直径为多少。

输入

第一行给出两个整数n和s ,n,s<= 100000, n为节点数, s为树的总长度。
接下来,每行两个整数u,v,表示节点u和v之间有一条边相连。
 

输出

输出L,保留两位小数。

样例输入 Copy

6 4
1 2
1 3
1 4
4 5
4 6

样例输出 Copy

2.00
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MAXN 1000011
ll deg[MAXN];
int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    ll n;
    double s;
    cin>>n>>s;
    //要分给每个叶子
    //每个叶子的贡献(经过他的路径数量)不会比其父亲多 
    for(ll i=1; i<n; ++i) {
        ll u,v;
        cin>>u>>v;
        ++deg[u],++deg[v];
    }
    ll cnt=0;
    //找叶节点 
    for(ll i=1; i<=n; ++i)
        if(deg[i]==1)
            ++cnt;
    printf("%.2lf",s*2.0/cnt);
    return 0;
}
 

问题 F: 求和

题目描述

等比数列是指从第二项起,每一项与它的前一项的比值等于同一个常数的一种数列。对于一个等比数列an=a1qn-1,它的前n项的和Sn=a1(1-qn)/(1-q)(q≠1)。现在已知A为n*n的矩阵,S=A+$A^2$+$A^3$+...+$A^m$,你能否正确求出S,并且输出S中的每一个元素对1000000007取模后的值。

输入

输入包括n+1行,第一行包括两个正整数n, m,分别代表矩阵A的大小和S中的项数,其中1≤n≤30, 1≤m≤109。接下来n行,每行n个元素,相应地代表A中的元素x,其中0≤x≤106。

输出

输出包括n行,每行n个元素,相应地代表S中的每一个元素对1000000007取模后的值。

样例输入 Copy

1 2019
1

样例输出 Copy

2019

矩阵乘法+快速幂
#include <iostream>
#include <cstring>
using namespace std;
struct matrix {
    int data[35][35];
};
int n = 0;
int m = 0;
int k = 0;
//矩阵乘法
matrix mul(matrix a, matrix b) {
    matrix c;
    memset(c.data, 0, sizeof(c.data));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                c.data[i][j] = (c.data[i][j] + 1ll * a.data[i][k] * b.data[k][j]) % m;
            }
        }
    }
    return c;
}
//矩阵加法
matrix add(matrix a, matrix b) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            a.data[i][j] = (a.data[i][j] + b.data[i][j])%m;
        }
    }
    return a;
}
//矩阵快速幂
matrix quickpow(matrix a, int k) {
    matrix  c;
    memset(c.data, 0, sizeof(c.data));
    for (int i = 1; i <= n; i++)
        c.data[i][i] = 1;
    while (k) {
        if (k & 1) c = mul(c, a);
        k >>= 1;
        a = mul(a, a);
    }
    return c;
}
//正式计算 sum k
matrix sum(matrix a, int k) {
    if (k == 1) return a;
    matrix c;
    memset(c.data, 0, sizeof(c.data));
    for (int i = 1; i <= n; i++)
        c.data[i][i] = 1;
    c = add(c, quickpow(a, k >> 1));
    c = mul(c, sum(a, k >> 1));
    if (k & 1) c = add(c, quickpow(a, k));
    return c;
}
int main() {
    matrix mat;
    cin >> n;
    cin >> k;
    m=1000000007;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> mat.data[i][j];
        }
    }
    matrix ret = sum(mat, k);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << ret.data[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

问题 G: 奎奎画画

题目描述

“为你写诗,为你静止,为你做不可能的事”,爱情是一种怪事,它让奎奎开始学习画画。奎奎认为一张画的艺术价值等于画上的白色联通块个数(当一个格子和它上下左右四个方向上的某个相邻格子颜色相同,则认为它们属于同一个联通块),奎奎还认为他作画的艺术价值和妹子对他的好感度紧密相关,因此奎奎非常在意每一时刻他的画的艺术价值。 为了简化题目,奎奎在一张n行m列的白色矩形格子画布上作画,他一共画了q笔,每一笔都是从(x1,y1)格子开始到(x2,y2)格子结束(x1=x2或y1=y2),将所有满足x1<=x<=x2并且y1<=y<=y2的格子涂黑。奎奎想知道当他画完每一笔之后,这幅画的艺术价值是多少。

输入

第一行三个整数n,m,q (1≤n, m≤1000, 1≤q≤10000 )
下面q行每行4个整数x1,y1,x2,y2 (1≤x1≤x2≤n, 1≤y1≤y2≤m)描述奎奎画的q条线段的起点和终点
 

输出

q行,对于奎奎画的每一条线段,输出一行一个整数表示该线段画完之后画布上白色联通块的个数。

样例输入 Copy

4 6 5
2 2 2 6
1 3 4 3
2 5 3 5
4 6 4 6
1 6 4 6

样例输出 Copy

1
3
3
4
3
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MAXN 1011
int read() {
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')             //判断正负
        flag=1;
    else if(ch>='0'&&ch<='9')           //得到完整的数
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}
ll fa[MAXN*MAXN];
ll find(ll x) {
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
bool uni(ll u,ll v) {
    u=find(u),v=find(v);
    if(u==v)
        return 0;
    fa[u]=v;
    return 1;
}
struct opt {
    ll x1,y1,x2,y2;
} a[MAXN*10];
ll c[MAXN][MAXN],ans=0;
const ll mx[]= {0,1,0,-1},my[]= {1,0,-1,0};
ll n,m,Q;
void ctrb(ll x,ll y) {
    for(ll i=0; i<4; ++i) {
        ll vx=x+mx[i],vy=y+my[i];
        //如果在一个并查集里面,就不用减去
        //如果不在的话,说明相邻的有白块,直接合并,减去下面多加的次数 
        if(vx>0&&vx<=n&&vy>0&&vy<=m&&!c[vx][vy])
            ans-=uni((x-1)*m+y,(vx-1)*m+vy);
    }
}
ll p[MAXN*10];
int main() {
    //从Q~1处理每个询问
    //离线的时候顺便用二维差分算一下覆盖次数
    //然后对于每个出现次数为0的点,都和相邻点合并(如果相邻点没被覆盖)
    //顺便统计连通块数量
    //然后倒着,把覆盖次数减掉
    //新的出现次数为0的点也合并一下就好了 
    n=read(),m=read(),Q=read();
    for(ll i=1; i<=Q; ++i) {
        ll x1=read(),y1=read(),x2=read(),y2=read();
        //二维差分处理 
        ++c[x1][y1],--c[x2+1][y1],--c[x1][y2+1],++c[x2+1][y2+1];
        a[i]=opt {x1,y1,x2,y2};
    }
    //二位差分处理 
    for(ll i=1; i<=n; ++i)
        for(ll j=1; j<=m; ++j) {
            c[i][j]+=c[i-1][j]+c[i][j-1]-c[i-1][j-1];
            //并查集维护 
            fa[(i-1)*m+j]=(i-1)*m+j;
        }
    //如果是0,那么就说明还没有被覆盖过, 统计连通块的数量 
    for(ll i=1; i<=n; ++i)
        for(ll j=1; j<=m; ++j)
            if(!c[i][j])
                ++ans,ctrb(i,j);
    for(ll i=Q; i; --i) {
        p[i]=ans;
        ll x1=a[i].x1,y1=a[i].y1,x2=a[i].x2,y2=a[i].y2;
        //倒着处理,覆盖次数-- 
        for(ll x=x1; x<=x2; ++x)
            for(ll y=y1; y<=y2; ++y)
                --c[x][y];
        //统计块数 
        for(ll x=x1; x<=x2; ++x)
            for(ll y=y1; y<=y2; ++y)
                if(!c[x][y])++ans,ctrb(x,y);
    }
    for(ll i=1; i<=Q; ++i)printf("%lld
",p[i]);
    return 0;
}

问题 H: qiqi and sciorz

题目描述

一天,qiqi和sciorz很无聊,他们又玩起来更无聊的取石子游戏,游戏规则是这样的:

有一堆n个石子,qiqi先取,每次最少取一个,第一次取的时候最多取n-1个,之后每次不能超过上一次的k倍,取得最后一个石子的为胜利,也就是说不能操作的为败.

然而sciorz早已洞穿了一切,他已经知道了谁会胜利,并且他急着去聊妹子,于是他开始催促qiqi快点开始,但是qiqi并不会玩,你能帮帮他吗?
 

输入

一行一个整数T表示玩了T局(T<=1000)

之后T行,每行两个整数n,k.

 

输出

如果qiqi输了,输出一个字符串"qiqi lose"。

如果sciorz输了,输出qiqi第一次应该取走多少个。

题目保证不会有第三种结局
 

样例输入 Copy

2
2 1000
3 1

样例输出 Copy

qiqi lose
1

提示

第一个样例,qiqi 第一步只能取走1个石子,然后sciorz取走最后一个,qiqi 输了。
第二个样例,qiqi 第一步取走1个石子,然后sciorz取走一个,qiqi取走最后一个,qiqi获得了胜利
对于25%的数据有n<=10,k<=10
对于另外25%的数据有n<=10
对于另外25%的数据有k<=10
对于100%的数据有n<=100000,k<=100000
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')             //判断正负
        flag=1;
    else if(ch>='0'&&ch<='9')           //得到完整的数
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}
#define MAXN 100011
ll f[MAXN],t[MAXN];
int main()
{
    ll task=read();
    while(task--)
    {
        ll n=read(),k=read();
        f[1]=t[1]=1;
        ll len=1,it=1;
        while(n>f[len])
        {
            ++len;
            f[len]=t[len-1]+1;
            while(f[it+1]*k<f[len])++it;
            if(f[it]*k<f[len])t[len]=t[it]+f[len];
            else t[len]=f[len];
        }
        if(n==f[len])
        {
            puts("qiqi lose");continue;
        }
        while(n)
        {
            if(n>=f[len])
            {
                n-=f[len];
                if(!n)
                {
                    printf("%lld
",f[len]);
                    break;
                }
            }
            --len;
        }
    }
    return 0;
}

问题 I: 星区划分

题目描述

爱好天文的小七发明了一种太空分区方法,在这个方法中,宇宙里亮度相近的星星被划为同一个星区。空间中任意相邻或坐标相同的星星若亮度差不大于给定整数M,则这两个星星属于同一星区(两个星星可能会有相同的坐标)。

现给你一个空间n个星星的三维坐标(x,y,z),和亮度值v,每个星星的上、下、左、右、前、后的六个相邻位置被认为是与其相邻的。请你计算一下该空间内的星区数量。
 

输入

第一行1个正整数n。n<=1000  
第二行 1个正整数m。m<=109  
第二行到第n+1行,每行四个正整数x,y,z,v。0<=x,y,z,v<=109 

输出

一行正整数k,代表星区数量。
 

样例输入 Copy

5   
2  
1 1 1 2  
1 2 3 2  
1 2 2 5  
1 1 0 3  
1 0 1 3

样例输出 Copy

3

#include<iostream>
#include<cmath>
using namespace std;
#define rep_1(i,m,n) for(int i=m;i<=n;i++)
#define mem(st) memset(st,0,sizeof st)
int read() {
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')             //判断正负
        flag=1;
    else if(ch>='0'&&ch<='9')           //得到完整的数
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;  //int无穷大值1061109567

const int N=101010;
struct node {
    ll x,y,z,w;
} e[N];
ll fa[N];
ll n,m;
bool check(node a,node b) {
    if(abs(a.w-b.w)>m)
        return false;
    return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z)<=1;
}
ll find(ll x) {
    if(fa[x]==x)
        return x;
    return fa[x]=find(fa[x]);
}
void uni(ll u,ll v) {
    u=find(u);
    v=find(v);
    fa[u]=v;
}
int main() {
    cin>>n>>m;
    for(ll i=1; i<=n; i++)
        cin>>e[i].x>>e[i].y>>e[i].z>>e[i].w;
    for(ll i=1; i<=n; i++)
        fa[i]=i;
    for(ll i=1; i<=n; i++)
        for(ll j=1; j<=n; j++)
            if(check(e[i],e[j]))
                uni(i,j);
    ll ans=0;
    for(ll i=1; i<=n; i++)
        if(find(i)==i)
            ans++;
    cout<<ans<<endl;
    return 0;
}

问题 J: 加油2020

题目描述

由于Slanely的队友们都不会数论,Slanely研究数学研究得头都快秃了。
Slanely最近在研究狄利克雷卷积,狄利克雷卷积是指:设f(x)是一个数论函数,g(x)也是一个数论函数,则h(n)也是一个数论函数,其定义为。Slanely看到这个定义就感觉头皮发麻,于是就准备出一道相关的题来难为新生。设f(x)为x的因子数,g(x)为x的因子和,求其狄利克雷卷积,即。尴尬的是,Slanely发现他并不会做这道他出的题,于是他只能修改了题目。修改后的题目如下:

设f(x)为x的因子数,g(x)为x的因子和,求 。由于答案很大,你需要对结果取模p。
 

输入

一行,两个整数,分别表示n和p, n<=1000,p<1000000000000000000,保证p是一个质数,且p>=673。。

输出

输出对p求模的结果

样例输入 Copy

242 1000000007

样例输出 Copy

349885024


问题 K: 数学问题

题目描述

我们高中曾经学过何为组合数。 那么,给出整数n,m,g,聪明的你能否求出有多少整数对(i,j),满足g整除吗?(其中0≤i≤n,0≤j≤min(i,m))。 (提示:n!=1×2×⋯×n;特别地,0!=1。)

输入

第一行一个整数T(T<=104 ),表示测试数据的组数;
第二行一个整数g(1<g<=25);
接下来T行每行两个整数n,m(n,m<=2000);
n,m,g的意义见题目描述。
 

输出

输出T行,每行一个整数,表示有多少对整数对(i,j)满足g整除(0≤i≤n,0≤j≤min(i,m))。
 
noip原题
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,k,n,m;
int c[2005][2005],s[2005][2005];
void prepare();
int main() {
    memset(c,0,sizeof(c));
    memset(s,0,sizeof(s));
    cin>>t>>k;
    prepare();
    while(t--) {
        cin>>n>>m;
        if(m>n) m=n;
        cout<<s[n][m]<<endl;
    }
    return 0;
}
void prepare() {
    c[1][1]=1;
    for(int i=0; i<=2000; i++) c[i][0]=1;
    for(int i=2; i<=2000; i++) {
        for(int j=1; j<=i; j++) {
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
        }
    }
    for(int i=2; i<=2000; i++) {
        for(int j=1; j<=i; j++) {
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
            if(c[i][j]==0) s[i][j]+=1;
        }
        s[i][i+1]=s[i][i];
    }
}

问题 L: 迪杂斯特

题目描述

在A国,一年有50!=1×2×...×50天,每天的编号从1到50!。
A国的土地上经常发生自然灾害。据A国科学家的妍究,A国有n种自然灾害,每种自然灾害的发生都有周期性。
具体地,第i种自然灾害可以用一个只包含Y和N组成的字符串组成。
设这个字符串的长度为L。如果这个字符串的第j个字符为Y,那么这就表示对于所有的k∈N,在第k×L+j天会发生第i种自然灾害,否则不会发生。
一天同时发生的自然灾害种数决定了该天整个A国境内的危险程度。
所以,你需要对于所有的0≤i≤n,求出一年内有多少天同时发生的自然灾害种数恰好为i。

输入

第一行一个正整数n。
接下来n行,每行一个Y和N组成的字符串,描述一种自然灾害。

输出

输出n+1行,其中第i行表示一年内,同时发生的自然灾害种数恰好为i的天数对10007取模后的结果。

样例输入 Copy

【样例1】
2
YNN
NYNN
【样例2】
4
NYYYYNNYYYYNNNNNYYNNNYYNNYNNNNNYNYYYNNYNYNYYNNYYYN
NYYYYNYNNNYNNNYNYNYNYYY
YNNNYNNNYYNYNYNNYNYYYNNN
YYNNNNYYYYNYYNYNYYNYNNYYYNNNYYNNYYNYN

样例输出 Copy

【样例1】
6621
514
6107
【样例2】
3595
3506
5771
9830
547

提示

对于10%的数据n=2;
对于20%的数据所有字符串长度的乘积不超过1111111;
另有20%的数据所有字符串长度两两互质或相等;
另有30%的数据所有字符串长度不超过48;
对于100%的数据,所有字符串长度不超过50,n≤30。
原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12446909.html