【51nod】1776 路径计数

【51nod】1776 路径计数

我们先把前两种数给排好,排好之后会有\(a + b + 1\)个空隙可以填数,我们计算有\(k\)个空隙两端都是相同字母的方案数

可以用枚举把第二种数分成几段插进去来算,设这个方案数为\(f[k]\)

然后对于一种有\(k\)个空隙的方案数,枚举剩下的\(a + b + 1 - k\)个空隙填了\(h\)个

然后计算把\(C\)和\(D\)分成\(k + h\)段的方案数就好了,记为\(g[k + h]\)

那么如何计算\(g[i]\)呢

一段要么是偶数,\(C\)和\(D\)个数相等,一段要么是奇数,\(C\)多或者\(D\)多

对于一个\(i\),强制认为\(C\)多的有\(j\)个,那么可以求出\(D\)多的是\(k\)个,剩下的必须两两配对,把剩下的\(i - j - k\),填成\(DC\)或者\(CD\),有两种方案

然后剩下的\(CD\)的对子,扔到\(i\)个区间中排列方法是唯一的,只要计算切成\(i\)段每段可以为空的方案数是多少就行了,是个组合数

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define eps 1e-10
#define ba 47
#define MAXN 1000005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 +c - '0';
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}
const int MOD = 998244353;
int fac[10005],invfac[10005],f[5005],g[5005],pw[5005];
int a[5],N;
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
void update(int &x,int y) {
    x = inc(x,y);
}
int fpow(int x,int c) {
    int res = 1,t = x;
    while(c) {
        if(c & 1) res = mul(res,t);
        t = mul(t,t);
        c >>= 1;
    }
    return res;
}
int C(int n,int m) {
    if(n < m) return 0;
    return mul(fac[n],mul(invfac[m],invfac[n - m]));
}
int main(){
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    fac[0] = 1;
    for(int i = 1 ; i <= 10000 ; ++i) fac[i] = mul(fac[i - 1],i);
    invfac[10000] = fpow(fac[10000],MOD - 2);
    for(int i = 9999 ; i >= 0 ; --i) invfac[i] = mul(invfac[i + 1],i + 1);
    pw[0] = 1;
    for(int i = 1 ; i <= 5000 ; ++i) pw[i] = mul(pw[i - 1],2);
    while(scanf("%d%d%d%d",&a[0],&a[1],&a[2],&a[3]) != EOF) {
        N = a[0] + a[1] + a[2] + a[3];
        if(!N) {puts("1");continue;}
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(int i = 1 ; i <= a[2] + a[3] ; ++i) {
            for(int j = 0 ; j <= min(i,a[2]) ; ++j) {
                if(a[3] < a[2] - j) continue;
                int k = a[3] - (a[2] - j);
                if(j + k > i) continue;
                if(a[2] - j < i - j - k) continue;
                int t = 1;
                t = mul(t,C(i,j));t = mul(t,C(i - j,k));
                t = mul(t,pw[i - j - k]);
                int rem = a[2] - j - (i - j - k);
                t = mul(t,C(rem + i - 1,i - 1));
                update(g[i],t);
            }
        }
        for(int i = 1 ; i <= a[1] ; ++i) {
            int t = C(a[1] - 1,i - 1);
            for(int k = max(0,i - 2) ; k <= min(a[0] - 1,i) ; ++k) {
                int t0 = t;
                t0 = mul(C(a[0] - 1,k),t0);t0 = mul(t0,C(2,i - k));
                int p = a[0] - 1 - k + a[1] - i;
                update(f[p],t0);
            }
        }
        int ans = 0;
        for(int i = 0 ; i <= a[0] + a[1] ; ++i) {
            if(!f[i]) continue;
            for(int h = a[0] + a[1] + 1 - i ; h >= 0 ; --h) {
                int c = mul(f[i],g[i + h]);
                c = mul(c,C(a[0] + a[1] + 1 - i,h));
                update(ans,c);
            }
        }
        out(ans);enter;
    }
}

原文地址:https://www.cnblogs.com/ivorysi/p/11073565.html

时间: 2024-11-04 02:24:41

【51nod】1776 路径计数的相关文章

51nod 1682 中位数计数

1682 中位数计数基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数. 现在有n个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数. Input 第一行一个数n(n<=8000) 第二行n个数,0<=每个数<=10^9 Output N个数,依次表示第i个数在多少包含其的区间中是中位数. Input示例 5 1 2 3 4 5

51 nod 1610 路径计数(Moblus+dp)

1610 路径计数 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 路径上所有边权的最大公约数定义为一条路径的值. 给定一个有向无环图.T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模). Input 第一行两个整数n和m,分别表示有向无环图上的点数和边数.(1<=n<=100,1<=m<=50,000) 第2~m+1行每行三个数x,y,z,表示有一条从x到y权值为z的边.(1

51nod 1682 中位数计数(前缀和)

51nod 1682 中位数计数 思路: sum[i]表示到i为止的前缀和(比a[i]小的记为-1,相等的记为0,比a[i]大的记为1,然后求这些-1,0,1的前缀和): hash[sum[i]+N](由于sum[i]会小于0,所以要加N)记录在j<i的情况下sum[i]是否出现过,如果在j>=i的情况下,sum[i]还出现过,那么就代表有一个区间满足条件. 代码: #include<bits/stdc++.h> using namespace std; #define ll lo

洛谷 P1176 路径计数2

P1176 路径计数2 题目描述 一个N×N的网格,你一开始在(1, 1),即左上角.每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N, N),即右下角有多少种方法. 但是这个问题太简单了,所以现在有M个格子上有障碍,即不能走到这M个格子上. 输入输出格式 输入格式: 输入文件第1行包含两个非负整数N,M,表示了网格的边长与障碍数. 接下来M行,每行两个不大于N的正整数x, y.表示坐标(x, y)上有障碍不能通过,且有1≤x, y≤n,且x, y至少有一个大于1,并请注意障碍坐标有可

HDU 6116 路径计数

HDU 6116 路径计数 普通生成函数常用于处理组合问题,指数生成函数常用于处理排列问题. 考虑 对于 $ a $ 个 $ A $ 分为很多堆,这么分的方案数是 $ C_{a-1}^{i-1} $ 然后对于每一堆我们看成一个数来放,并且所有堆都这样做,这样的话总的方案数量是 $ \frac{(i+j+k+l)!}{i!j!k!l!} $ 就算所有一堆看成的数的排列是不存在相邻相等的,至少都有 $ n-i-j-k-l $ 对相邻的相同的数. 然后就可以容斥了,枚举 $ i+j+k+l $ 直接计

[51nod1610]路径计数

路径上所有边权的最大公约数定义为一条路径的值. 给定一个有向无环图. T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模). Input 第一行两个整数n和m,分别表示有向无环图上的点数和边数.(1<=n<=100,1<=m<=50,000) 第2~m+1行每行三个数x,y,z,表示有一条从x到y权值为z的边.(1<=x,y<=n,1<=z<=100) 第m+2行一个数T,表示修改操作次数(

51nod 1967 路径定向(不错的欧拉回路)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1967 题意: 思路: 出度=入度,这很容易想到欧拉回路,事实上,这道题目也确实是用欧拉回路来做的,之前一直觉得应该用网络流来做,可惜想不出,后来看官方题解说也是可以的,但是复杂度太高. 对于每条边,先假设它为无向边,奇点的个数肯定是偶数个,对于这些奇点,我们可以两两连条边,使它们变成偶点,这样一来就肯定存在欧拉回路了,跑一遍就可以了.新加的边是不会影响结果的. 这道题目

51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

 区间计数 基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下: Ans:=Σni=1Σnj=i[max{Ai,Ai+1,...,Aj}=max{Bi,Bi+1,...,Bj}] 注:[ ]内表达式为真,则为1,否则为0. 1≤N≤3.5×1051≤Ai,Bi≤N 样例解释: 7个区间分别为:(1,4),(1,5),(2,4),(2,5),(3,3),(3,5),(4,5) Input 第一行一个整数N 第二行

51nod 1443 路径和树——最短路生成树

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1443 不只是做一遍最短路.还要在可以选的边里选最短的才行. 以为是求一遍最短路,然后按边权对边排序,哪条边两边的dis正好吻合,就把该边的边权加到ans里,把两端加到并查集里. 但其实不对.因为忽略了方向.比如如果有多个点同样地可以更新一个点,算的时候可能这多个点都因为那个点而被合到了并查集里,但其实只能有一个被合进去. 其实只要松弛点的时候如果dis[v]==d