ABC405

E - Fruit Lineup

问题陈述

你有 A 个苹果、 B 个桔子、 C 根香蕉和 D 粒葡萄。
要把这些 A+B+C+D 水果从左到右排成一行,使下面的条件全部成立,一共有多少种排列方法?对998244353取模

条件:

每个苹果都放在每根香蕉的左边。
每个苹果都放在每个葡萄的左边。
每个桔子都放在每个葡萄的左边。

在这里,苹果、橘子、香蕉和葡萄都是无法区分的。

赛时思路

无(想到组合数,但组合数写挂了,所以赛时一直在调组合数)

正解思路

发现题目的限制形式化的可以归结于以下三点
顺序要求:
ac
ad
bd

于是我们可以将整堆水果分成ab bc cd三堆(这里一堆内的水果可以随意排列)

枚举cd中c的数量c1,则cd中的排列方法cnt有C(c1+d-1,c1)种(c1+d-1是固定最后一个为葡萄,防止重复计数)

假设bc中b为b1个,ab中b为b2=b-b1个,已知c为c2=c-c1个,那么当前方案的贡献为:

cnt * C(b1+c2,b1) * C(a+b2,b2)

然而这里又要枚举b1从0到b

于是考虑范德蒙德卷积



于是原来的答案变为:

cnt*C(a+b+c2,b)

可直接求

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD=998244353;
int fac[3000005],infac[3000005]; 
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
int CC(int n,int m){
	return fac[n]*infac[m]%MOD*infac[n-m]%MOD;
}
signed main()
{
	int A,B,C,D;
	cin>>A>>B>>C>>D;
	int n=A+B+C+D;
	fac[0]=1;
	for(int i=1;i<=n;i++){
		fac[i]=(fac[i-1]*i)%MOD;
	}
	infac[n]=ksm(fac[n],MOD-2,MOD);
	for(int i=n-1;i>=1;i--){
		infac[i]=(infac[i+1]*(i+1))%MOD;
	}
	infac[0]=1;
	int ans=0;
	for(int i=0;i<=C;i++){
		int cnt=CC(D+i-1,i)%MOD*CC(A+B+C-i,B)%MOD;
		ans+=cnt;
		ans%=MOD;
	}
	cout<<ans<<'\n';
	return 0;
}


F - Chord Crossing


呜呜自己太菜了,F一点没有思路/ll

正解思路:

先考虑什么时候可能会相交

所以问题转化成在平面直角坐标系上M次询问每次询问一个点(ci,di),问它是否在多少个矩形内

可以离线做二维数点

具体的,先进行二维差分(一般套路),把下界抹掉

问题进一步变为

对其一维进行排序

若相同需要保证加入在前,询问在后

需要树状数组(值域线段树)维护加入、询问有多少的值<=Bj(值域线段树上其实就是询问前缀和)

(三维数点:CDQ分治:按第一维排序,第二维CDQ分治,合并时归并排序,第三维插入树状数组)

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;

struct Node{
    int x, y, id, d;// d: 对答案的贡献是1还是-1

    bool operator < (const Node &t) const {
        if(x != t.x) return x < t.x;
        return id < t.id;
        // 点的id都是 -1,让询问的id是正数
    }
};

vector<Node> vec;
int ans[MAXN];

struct BIT{
    #define lowbit(x) ((x)&(-(x)))
    int tree[MAXN];

    void add(int p,int d){
        while(p < MAXN){
            tree[p] += d;
            p += lowbit(p);
        }
    }

    int query(int p){
        int res = 0;
        while(p){
            res += tree[p];
            p -= lowbit(p);
        }
        return res;
    }
}bit;

int n, m, q;

int main(){
    scanf("%d%d",&n,&m);n *= 2;
    for(int i = 1;i <= m;++i){
        int c, d;
        scanf("%d%d",&c,&d);
        vec.push_back({c, d, -1, 1});
        vec.push_back({d, c, -1, 1});
    }
    scanf("%d",&q);
    for(int i = 1;i <= q;++i){
        int a, b;
        scanf("%d%d",&a,&b);
        // [a,b] x [1,a]
        vec.push_back({b, a, i, 1});
        if(a-1) vec.push_back({a-1, a, i, -1});

        // [a,b] x [b,n]
        vec.push_back({b, n, i, 1});
        if(a-1) vec.push_back({a-1, n, i, -1});
        if(b-1) vec.push_back({b, b-1, i, -1});
        if((a-1) && (b-1))vec.push_back({a-1, b-1, i, 1});
    }
    sort(vec.begin(), vec.end());

    for(auto [x, y, id, d] : vec){
        if(id == -1){
            bit.add(y, d);
        }
        else{
            ans[id] += d * bit.query(y);
        }
    }

    for(int i = 1;i <= q;++i){
        printf("%d\n",ans[i]);
    }
    return 0;
}
/*
4 2
2 4
6 8
1
3 7
*/
posted @ 2025-05-10 22:54  gbrrain  阅读(109)  评论(5)    收藏  举报