2024杭电暑假第一场——1012并

思想:我们用所有矩形x轴值(所有矩形的左右边所在的x轴位置),把坐标轴划分成一个个小段。

我们暂且把一个矩形的下边界认为是“入边”,上边界认为是“出边”。

我们从左往右(外层循环以为x轴增长方向遍历左右边),从下往上(内层循环为以y轴增长方向遍历上下边)对边进行遍历。如遇到的是下边界则说明这个边的上面比下面多一个矩形覆盖,遇到上边界则说明这个边的上面比下面少一个矩形覆盖。我们把这条边上每个矩形覆盖数所对应的覆盖长度记下来,再乘以这条左右边到下条左右边的宽度,就能算出这段中每个矩形覆盖数所对应的面积。

如下图例子所示,绿色边为下边界,红色边为上边界,橙色数字为这段长度所对应的矩形覆盖数,蓝色虚线为当前左右边,深蓝色为下一条左右边,这里两条边的距离为 2 2 2 , g [ i ] g[i] g[i] i i i 个矩形覆盖的面积之和。

在这里插入图片描述

算完所有 g [ i ] g[i] g[i] 后我们就要算期望了。因为要算选 k k k 个矩形的面积期望,我们便要考虑矩形覆盖所带来的面积减少的影响。为了方便思考,不如我们以数格子的思想来想:选到这 i i i 个矩形覆盖其中一个的概率,然后再乘以它的面积,这就是它的期望。因为这个选到 i i i 个矩形覆盖的概率比较难求,我们不如算“不取这 i i i 个矩形的概率 C n − i k C n k \frac{C_{n - i}^{k}}{C_{n}^{k}} CnkCnik",再 1 − C n − i k C n k 1 - \frac{C_{n - i}^{k}}{C_{n}^{k}} 1CnkCnik,就是取到这个 g [ i ] g[i] g[i] 所占有面积(格子)的概率。然后把所有情况连加起来,就有公式
a n s = ∑ i = 1 n ( ( 1 − C n − i k C n k ) ∗ g [ i ] ) ans = \sum_{i = 1}^{n}((1 - \frac{C_{n - i}^{k}}{C_{n}^{k}}) * g[i]) ans=i=1n((1CnkCnik)g[i])

顺序输出每个 1 − n 1-n 1n 的所算出的 a n s ans ans 即可。

const int N = 2e3+5;
const int MOD = 998244353;

int c[N][N];									//组合数

void init(){									//初始化
    c[0][0] = c[1][0] = c[1][1] = 1;
    for(int i = 2;i < N;++i){
        c[i][0] = 1;
        for(int j = 1;j <= i;++j){
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
        }
    }
}

int qpow(int x,int n){		
    int ans = 1;
    while(n){
        if(n & 1)ans = (ans * x) % MOD;
        x = (x * x) % MOD;
        n >>= 1;
    }
    return ans % MOD;
}


struct lx{
    int y,x1,x2,flag;
    bool operator<(const struct lx & that)const{
        return y < that.y;
    }
};

struct ly{
    int x,y1,y2,flag;
    bool operator<(const struct ly & that)const{
        return x < that.x;
    }
};

void solve(){
    int n;cin >> n;
    vector<struct lx>linex;				//存于x轴平行的边,也就是左右边 
    vector<struct ly>liney;				//存于y轴平行的边,也就是上下边

    init();

    int x1,y1,x2,y2;
    for(int i = 0;i < n;++i){
        cin >> x1 >> y1 >> x2 >> y2;
        linex.push_back({y1,x1,x2,1});			//左边为入边 标记为1
        linex.push_back({y2,x1,x2,0});			//右边为出边 标记为0
        liney.push_back({x1,y1,y2});
        liney.push_back({x2,y1,y2});
    }
    sort(linex.begin(),linex.end());
    sort(liney.begin(),liney.end());

    vector<int>g(n + 1);
    vector<int>tmp(n + 1);
    int lowx = 0;									//用于记录上一条边的x轴位置 算宽用
    bool mark1 = 1;

    for(auto [x,y1,y2] : liney){					//从左往右遍历平行于y轴的边
        if(mark1 == 0){								//特判  刚遍历第一条边时不该进行算面积操作
            for(int i = 1;i <= n;++i){
                g[i] += tmp[i] * (x - lowx) % MOD;
                g[i] %= MOD;
            }
        }
        tmp = vector<int>(n + 1);					//用于暂存 被i个矩形覆盖的边 的长度
        lowx = x;
        mark1 = 0;
        bool mark2 = 1;
        int lowy = 0;	
        int cnt = 0;								//当前时被几(cnt)个矩形覆盖
        for(auto [y,x1,x2,flag2]:linex){

            if(x1 <= x && x < x2){
                if(flag2) 	//flag2 == 1 为入边 cnt++   否则为出边 cnt--
                    if(mark2){					//特判  刚遍历第一条边时cnt刚从0加到1,0无意义所以不存
                        mark2 = 0;
                        lowy = y;
                        cnt++;
                    }
                    else{
                        tmp[cnt] += y - lowy;
                        tmp[cnt] %= MOD;
                        cnt++;					
                        lowy = y;
                    }
                }
                else{
                    tmp[cnt] += y - lowy;
                    tmp[cnt] %= MOD;
                    cnt--;
                    lowy = y;
                }
            }
        }
    }

    for(int k = 1;k <= n;++k){
        int ans = 0;
        int p = qpow(c[n][k],MOD - 2);					//预处理 逆元求C_{n}^{k}
        for(int i = 1;i <= n;++i){
            ans += ( (1 - (c[n - i][k]) * p % MOD + MOD) % MOD) * g[i];				//套公式
            ans %= MOD;
        }
        cout << ans % MOD << endl;
    }
}
  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值