ZROI1153 【线上训练3】数个数

ZROI1153 【线上训练3】数个数

传送门

一道非常有意思的题,涵盖了各种知识点。

首先,很显然,这是个容斥。容斥可以过掉\(30pts\)。

这里我们考虑容斥+DP。

我们令\(dp[i][j]\)代表对于前\(i\)个区间(区间排过序),上一个取的区间为\(j\)的方案数。

那么每次转移就是:
\[
dp[i+1][i+1]+=-dp[i][k]*(s^{len})\space (k<i+1)
\]
其中\(len\)是这两个区间的中间部分的长度。而负号代表容斥系数(长度+1,奇偶性改变,所以变成负的)。

这个\(dp\)相当于考虑了第\(i+1\)个区间接在以不同的区间结尾的方案后面,所以可以做到不重不漏。因为在一种方案后接上一个区间对答案多出的贡献取决于这个区间和上个区间之间的距离,所以只用像这样记录上个区间即可。

下面这份代码来自An_Account,能展示出\(n^2\)的\(dp\)过程:

#include <bits/stdc++.h>
using namespace std;
const int N = 200010, mod = 998244353;
struct Query {
    int l, r, b;
    bool operator < (const Query &b) const {
        return r < b.r || (r == b.r && l < b.l);
    }
} l[N];
int dp[N];
typedef long long LL;
inline int Pow(int x, int y) {
    int res = 1;
    for (; y; y >>= 1, x = (LL)x * x % mod) if (y & 1) res = (LL)res * x % mod;
    return res;
}
int main() {
    int n, m, s; scanf("%d%d%d", &n, &m, &s);
    for (int i = 1; i <= m; i++) scanf("%d%d%d", &l[i].l, &l[i].r, &l[i].b);
    sort(l + 1, l + m + 1);
    dp[0] = 1;
    for (int i = 1; i <= m; i++)
        for (int j = 0; j < i; j++) {
            if (l[j].r < l[i].l || l[j].b == l[i].b) {
                int t = (LL)dp[j] * Pow(s, max(0, l[i].l - l[j].r - 1)) % mod;
                dp[i] = (dp[i] - t + mod) % mod;
            }
        }
    int res = Pow(s, n);
    for (int i = 1; i <= m; i++)
        res = (res + (LL)dp[i] * Pow(s, n - l[i].r)) % mod;
    printf("%d\n", res);
}

而正解就是在他的基础上用线段树优化转移。

首先我们发现满足转移要求有两种情况:

  1. 颜色不相同,区间不相交
  2. 颜色相同

我们考虑分开考虑两种情况,

对于第一种情况,我们可以开一个前缀和。
\[
令i,j为两个区间的编号,i在j之后。\c[i]=\sum _{j.r<i.l} \frac{dp[j]}{s^{j.r+1}}
\]

这是因为上一个方程中的\(s^{len}\)可以这样转化:

\(s^{len}=s^{r-l+1}=\frac{s^{r+1}}{s^l}\)

因为对于一次转移,\(s^{r+1}\)都是一样的(都是当前区间的起始点),所以我们只要在\(dp[i]=-c[i]*s^{i.l}\)

然后把这个值插入在区间的右端点+1即可(查询的时候查询左端点)。

对于第二种情况,我们可以使用动态开点线段树。

因为在第一种情况里只少统计了颜色相等,区间重叠的情况。所以我们用线段树统计这种情况。

两个区间相交当且仅当第一个区间(前面的区间)的终点被第二个区间包含。那我们统计一下第二个区间内有多少个其他区间的右端点即可。因为两个区间重叠,所以在这一步只有一种方案,所以直接加上上个区间的\(dp\)值即可。(别忘了要取相反数)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define ll long long
using namespace std;
const int mod=998244353;
const int maxn=2e5+1000;
int n,m,s,ls[maxn*80],rs[maxn*80],idx,rt[maxn];
struct gg {
    int l,r,b;
}q[maxn];
ll c[maxn],sum[maxn*80],dp[maxn];
void update_t(int loc,int val) {
    for(int i=loc;i<=n;i+=i&-i)c[i]+=val,c[i]%=mod;
}
void push_up(int root) {
    sum[root]=0;
    if(ls[root])sum[root]+=sum[ls[root]];
    if(rs[root])sum[root]+=sum[rs[root]];
    sum[root]%=mod;
}
void update_s(int l,int r,int root,int val,int tar) {
    if(l==r){sum[root]=val;sum[root]%=mod;return;}
    int mid=(l+r)>>1;
    if(tar<=mid) {
        if(!ls[root])ls[root]=++idx;
        update_s(l,mid,ls[root],val,tar);
    }
    if(tar>=mid+1) {
        if(!rs[root])rs[root]=++idx;
        update_s(mid+1,r,rs[root],val,tar);
    }
    push_up(root);
}
ll query_t(int loc) {
    ll tot=0;for(int i=loc;i;i-=i&-i)tot+=c[i],tot%=mod;
    return tot;
}
ll query_s(int l,int r,int root,int tl,int tr) {
    if(tl<=l&&r<=tr)return sum[root];
    int mid=(l+r)>>1;ll tot=0;
    if(l<=mid&&ls[root])tot+=query_s(l,mid,ls[root],tl,tr);
    if(r>=mid+1&&rs[root])tot+=query_s(mid+1,r,rs[root],tl,tr);
    return tot%mod;
}
int ksm(int num,int t){
    int res=1;
    for(;t;t>>=1,num=(ll)num*num%mod) {
        if(t&1)res=(ll)res*num%mod;
    }
    return res;
}
vector<gg>col[maxn];
bool cop1(gg x,gg y){if(x.l==y.l)return x.r<y.r;return x.l>y.l;}
bool cop2(gg x,gg y){return x.l<y.l;}
int main() {
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++) {
        scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].b);
        col[q[i].b].push_back(q[i]);
    }
    m=0;
    for(int i=1;i<=s;i++) {
        sort(col[i].begin(),col[i].end(),cop1);
        int now=maxn;
        for(int j=0;j<col[i].size();j++) {
            if(col[i][j].r<now){q[++m]=col[i][j];now=col[i][j].r;}
        }
    }
    sort(q+1,q+1+m,cop2);
    dp[0]=1*ksm(s,mod-2);
    update_t(1,dp[0]);//初始化
    for(int i=1;i<=m;i++) {
        ll t=query_t(q[i].l)*ksm(s,q[i].l);t%=mod;
        if(rt[q[i].b])t+=query_s(1,n,rt[q[i].b],q[i].l,q[i].r);
        dp[i]=(-t)%mod;
        update_t(q[i].r+1,(dp[i]*ksm(ksm(s,q[i].r+1),mod-2)%mod));
        if(!rt[q[i].b])rt[q[i].b]=++idx;
          update_s(1,n,rt[q[i].b],dp[i],q[i].r);
    }
    ll ans=0;
    for(int i=1;i<=m;i++) {
        ans+=dp[i]*ksm(s,n-q[i].r);ans%=mod;
    }
    ans+=ksm(s,n);
    ans=(ans%mod+mod)%mod;
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/GavinZheng/p/11693105.html

时间: 2024-11-08 22:51:50

ZROI1153 【线上训练3】数个数的相关文章

CSDN挑战编程——《金色十月线上编程比赛第一题:小女孩数数》

金色十月线上编程比赛第一题:小女孩数数 题目详情: [金色十月线上编程比赛规则] 一个小女孩正在用左手手指数数,从1数到n.她从拇指算作1开始数起,然后,食指为2,中指为3,无名指为4,小指为5.接下来调转方向,无名指算作6,中指为7,食指为8,大拇指为9,如此反复.问最后会停在那个手指上?用编号1.2.3.4.5依次表示大拇指.食指.中指.无名指.小指. 输入格式: 输入多组数据.每组数据占一行,只包含一个整数n(1<=n<=1000000000). 输出格式: 每组数据占一行,只包含一个介

运维守护神——数十万线上机器的守护【门神】

随着京东云业务的飞速发展,其需要管理的物理机.虚机以及各类容器已经达到了数十万之巨,在如此数量如此庞大资源机如何管理的课题面前,京东云意识到必须开发自己的高效.安全.稳定的资源机管理系统,为京东云乃至整个京东集团各项业务的发展提供坚实可靠的后盾,"门神"系统在这种情况下应运而生,并在经过多次京东618.11.11等诸多重大活动的检验后,变得愈发成熟稳定. "门神"顾名思义,就是守护整个京东资源机云安全的守护神,是京东云平台自主研发的一套基于服务树角色授权的线上机器运

简单实用可线上应用的线程池组件

0 前言 线程池的组件网上很多,之前我自己也尝试写个一个demo,但这些组件一般都比较简单,没有完整的实现后台线程池组件应用的功能.因此,这里我们实现一个可以用在线上环境的线程池组件,该线程池组件具备线程池应用的特性,如下所示: 1. 伸缩性:即线程池中线程的个数应该是动态变化的.繁忙的时候可以申请更多的线程:空闲的时候则注销一部分线程. 2. 线程状态:线程池中对线程的管理引入睡眠.唤醒机制.当线程没有任务在运行时,使线程处于睡眠状态. 3. 线程管理:对线程池中线程的申请和注销,不是通过创建

Nginx、Tomcat线上环境优化配置

 Nginx.Tomcat线上环境优化配置 Nginx优化: Nginx安全方面的优化: 1. nginx安全优化,在nginx配置文件http标签段内添加"server_tokens  off"即可隐藏访问或者报错时提示web版本号信息. 2. server_tokens参数可以在http,server,location的位置添加 3. 还可以修改nginx的3个源码文件 4. 如还需要安全优化更改端口.用户. nginx 性能优化: 对于nginx配置文件中对优化比较有作用的一般为

微软线上笔试-2015-4-3(1,2题) Magic Box &amp;&amp; Professor Q&#39;s Software

写在前面: http://blog.csdn.net/michael_kong_nju/article/details/44872519 关于4.3号的微软线上挑战赛,感觉自己还是刷题刷少了,表现在几个方面:1. 编程经验不足.2. 算法的使用不灵活.所以下面还是要加强OJ的训练, 把Leetcode上的题多做做.后面又把4道题仔细的编写调试了一下,希望和我情况类似的同学也能加紧代码的训练. 1. 第一题的原题是: The circus clown Sunny has a magic box.

hdu1339郭大侠与线上游戏

地址:http://acm.uestc.edu.cn/#/problem/show/1339 题目: 郭大侠与线上游戏 Time Limit: 6000/2000MS (Java/Others)     Memory Limit: 125535/65535KB (Java/Others) Submit Status 曾在网络游戏告白,但对方是假冒女生的人妖,而对此有阴影的郭大侠,坚持把游戏和现实分得清清楚楚.有一天竟被网络游戏中的女玩家告白了,虽然在游戏中接受了亚子的表白而结婚,但仍然不敢确信亚

CSDN挑战编程——《金色十月线上编程比赛第二题:解密》

金色十月线上编程比赛第二题:解密 题目详情: 小强是一名学生, 同时他也是一个黑客. 考试结束后不久,他惊讶的发现自己的高等数学科目居然挂了,于是他果断入侵了学校教务部网站.在入侵的过程中,他发现了与成绩相关的内容是一个加密文件,这个文件由 n 个数构成,经过分析,这个加密文件的密钥为这 n 个数中二进制位数 1 最少的数.但由于数比较多,小强 希望你能帮他得到密钥,好在成绩公布之前将成绩改过来. 输入描述: 输入由多组数据构成,每组数据第一行为一个数 n(1<=n<=10^5),表示数的数量

“玲珑杯”线上赛 Round #17 河南专场

闲来无事呆在寝室打打题,没有想到还有中奖这种操作,超开心的 玲珑杯"线上赛 Round #17 河南专场 Start Time:2017-06-24 12:00:00 End Time:2017-06-24 14:30:00 Refresh Time:2017-06-24 14:48:00 Private A -- Sin your life Time Limit:1s Memory Limit:128MByte Submissions:529Solved:76 DESCRIPTION 给一个正

蚂蚁金服技术专家分享25个分布式缓存实践与线上案例

前言: 本文主要介绍使用分布式缓存的优秀实践和线上案例.这些案例是笔者在多家互联网公司里积累并形成的优秀实践,能够帮助大家在生产实践中避免很多不必要的生产事故. 一.缓存设计的核心要素 我们在应用中决定使用缓存时,通常需要进行详细的设计,因为设计缓存架构看似简单,实则不然,里面蕴含了很多深奥的原理,如果使用不当,则会造成很多生产事故甚至是服务雪崩之类的严重问题. 1.容量规划 缓存内容的大小缓存内容的数量淘汰策略缓存的数据结构每秒的读峰值每秒的写峰值2.性能优化 线程模型预热方法缓存分片冷热数据