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); }
括号序列
可怜不喜欢括号序列,但是她发现总是有人喜欢出括号序列的题。
为了让全世界都能感受到她的痛苦,她想要写一个转换器,它能把普通的小写字符串转换成长度相同的合法的括号序列。
在可怜的构思中,这样的转换器需要满足如下两个条件:
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); }