zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    2018-2019-2 实验三 敏捷开发与XP实践
    计算机网络课外实验一级 20175319江野
    2018-2019-2 《Java程序设计》第9周学习总结
    MyCP(课下作业,必做)
    [NOIP2012] 疫情控制
    [SPOJ2021] Moving Pebbles
    谁能赢呢?
    [HEOI2014] 人人尽说江南好
    [笔记] 巴什博弈
    [SCOI2008] 着色方案
  • 原文地址:https://www.cnblogs.com/sto324/p/11628557.html
Copyright © 2011-2022 走看看