NOIP 模拟 1006

矿石

众所周知,九条可怜家里有矿

你可以把可怜家的矿场抽象成一条数轴,可怜家有n种矿,第i种矿可以从[li,ri] 中的任意位置开采得到

这个暑假,地理老师给了 可怜一个列表:她的暑假作业就是收集齐这些矿石,为了保证可怜的安全,可怜的爸爸选定了m个相对安全的采矿点,第i个采矿点的坐标为ai,可怜只能选择其中一个采矿点开采她需要的矿石.

可怜是一个马虎的女孩子。暑假刚开始没多久,可怜就把老师的列表弄丢了,唯一的线索是,列表上的所有矿石都是可怜家有的,则一共有2^n-1种可能的列表.

现在想要知道,在所有的可能的任务列表中,有多少种是她能够在某一个安全的采矿点收集齐的.

n,m<=1e5,1<=li,ri,ai<=1e9

题解

主要的难点就是这样才能不重复计算贡献,因为区间连续,考虑记录每个矿点与上个相比新增的矿石种类dif和总共的矿石种类tot。要不一样就必须要在新增的里面至少选一个,原来有的不一定选。贡献为$(2^{dif}-1)*2*{tot-dif}$

但是就是没想到这个,今天改3号T2的时候发现60有类似的分有类似的想法(早点改就好了)。

考虑这个信息怎么维护,tot比较好维护,就用差分数组即可,对于dif考虑将每个区间的贡献给他覆盖的第一个矿场。

因为坐标很大,所以要离散化。给出dif的时候要判断是否有矿场被覆盖。

 

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=100005;
const int mod=998244353;
int n,m,cnt;
int l[maxn],r[maxn],pos[maxn];
int tot[maxn],dif[maxn];//dif:有多少区间第一次被i得到 
ll ans,pow2[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

int main(){
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    read(n);read(m);
    pow2[0]=1;
    for(int i=1;i<=n;i++) pow2[i]=pow2[i-1]*2%mod;
    for(int i=1;i<=n;i++) read(l[i]),read(r[i]);
    for(int i=1;i<=m;i++) read(pos[i]);
    sort(pos+1,pos+m+1);
    for(int i=1;i<=n;i++){
        int x=lower_bound(pos+1,pos+m+1,l[i])-pos;
        int y=upper_bound(pos+1,pos+m+1,r[i])-pos;
        tot[x]++;tot[y]--;
        if(x<y) dif[x]++;//保证有矿场,x!=y 
    }
    for(int i=1;i<=m;i++) tot[i]+=tot[i-1];
    for(int i=1;i<=m;i++) ans=(ans+pow2[tot[i]-dif[i]]*(pow2[dif[i]]-1)%mod)%mod;
    printf("%lld",ans);
}
A

 


括号序列

可怜不喜欢括号序列,但是她发现总是有人喜欢出括号序列的题。
为了让全世界都能感受到她的痛苦,她想要写一个转换器,它能把普通的小写字符串转换成长度相同的合法的括号序列。
在可怜的构思中,这样的转换器需要满足如下两个条件:
1. 结果的括号序列必须要是合法的,即左右括号必须要是相匹配的。
2. 对于一堆相匹配的左右括号,他们所在的位置原来的小写字母必须相同。
举例来说,对于字符串aabaab,()(())就是一个合法的答案,而()()()不满足第二个条件,(((())不满足第二个条件
可怜发现对于一个小写字符串,有时候有很多满足条件的括号序列,有些时候一个都没有 于是可怜给出了一个小写字符串,她想让你帮她算一下,有多少不同的子串可以转化为满足条件的括号序列

题解

改了题时候以为是一共有多少划分方案....

考虑暴力,枚举左端点,模拟括号匹配过程:与栈顶相同就弹栈,不然压如栈内。当栈空ans++

优化就是用一个栈从头扫一遍,求出每个点的栈内的hash值,可以发现当有两个hash值相同表明中间的字符清空了,那么就是一种合法的字串。

所以最后找出所有相同的hash值,对于有cnt个的hash值,对答案贡献就是c(2,n),因为只要选出两个他们中间就是合法的。

不要忘记最开始的空栈也要放进去。

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
const int maxn=1000005;
const int mod=998244353;
const ull base=10007;
int len,top,st[maxn];
ull hash[maxn];
char s[maxn];

int main(){
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1;i<=len;i++){
        if(s[i]==s[st[top]]) {top--;hash[i]=hash[st[top]];}
        else {st[++top]=i;hash[i]=hash[st[top-1]]*base+s[i];}
        //printf("%lld ",hash[i]);
    }
        
    sort(hash,hash+len+1);
    int cnt=1;ull now=hash[0];ll ret=0;
    for(int i=1;i<=len;i++)
     if(hash[i]==now) cnt++;
     else {
         ret+=1ll*cnt*(cnt-1)/2%mod;
         now=hash[i];
         cnt=1;
     }
    ret+=1ll*cnt*(cnt-1)/2%mod; 
    printf("%lld",ret%mod);
}
B

 

posted @ 2019-10-06 21:32  _JSQ  阅读(573)  评论(0编辑  收藏  举报