标签:mes some 前缀 lin queue n+1 line return using
给定一维空间内若干区间和一些点,要求选出其中某些区间(不能不选)使得存在一个点属于给定点集满足此点被每一个选出区间所包含
求选择的方案数
\(N<=10^5\)
对于每一个点考虑
将点按照位置从小到大排序
如果当前有A个区间包含此点,那么这个点对答案的贡献就是\(2^A-1\)
容易发现这样做会重复计算
简单容斥一下:
假设有B个区间既包含这个点也包含上一个点,那么此时的贡献就要减去\(2^B-1\)
感觉这样容斥有点像“点减边”容斥
考场上想复杂了,写假了,还是太菜了
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5,mod=998244353;
struct node{int l,r;}s[N];
struct cmpl{bool operator()(const node&x,const node&y){return x.l<y.l;}};
struct cmpr{bool operator()(const node&x,const node&y){return x.r>y.r;}};
int n,m,a[N],calc[N];
priority_queue<node,vector<node>,cmpr>pq;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)w=-1;
for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i)s[i].l=read(),s[i].r=read();
for(int i=1;i<=m;++i)a[i]=read();
calc[0]=1;for(int i=1;i<=n;++i)calc[i]=add(calc[i-1],calc[i-1]);
for(int i=0;i<=n;++i)calc[i]=dec(calc[i],1);
sort(s+1,s+n+1,cmpl());
sort(a+1,a+m+1);
int now=1,ans=0;
for(int i=1;i<=m;++i)
{
while(!pq.empty()&&pq.top().r<a[i])pq.pop();
int A=pq.size(),B;
for(;now<=n&&s[now].l<=a[i];++now)
if(a[i]<=s[now].r)pq.push(s[now]);
B=pq.size();
ans=add(ans,dec(calc[B],calc[A]));
}
cout<<ans;
return 0;
}
给定一个长度为N的由小写字母组成的字符串,询问有多少子串满足如下条件:
存在一种和这个子串长度相等的合法的括号序列满足对于每一对相匹配的左右括号,其对应的小写字母是相同的(直接按照位置对应)
\(N<=10^6\)
先考虑对于一个字符串判断它本身是否满足条件
显然地,搞一个栈从左至右扫描。
若当前元素和栈顶元素相等则弹栈,否则压进栈中
若最后栈为空,那么这个串本身就是满足条件的
于是乎我们就得到了一个优秀的\(O(n^2)\)算法
如何优化呢?
我们搞一个类似于前缀和的前缀栈,第i个位置的栈记为\(sta_i\)
容易发现子串[l,r]满足条件的充要条件就是\(sta_{l-1}==sta_{r}\)
我们用哈希维护这个东西
扫描结束后如果哈希值为x的有p个,那么它对答案的贡献就是\((_2^p)\)
相当于就是任意选择两个作为左右端点
其实这种想法是有来头的:将一个区间内的数之和为0转化为某两个前缀和相等
实在是异曲同工之妙啊
用了双哈希才搞过。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5,base1=37,mod1=19930219,base2=31,mod2=91203991;
int n,hsh1[N],hsh2[N];
ll ans;
map<pair<int,int>,ll>mp;
char ch[N];
stack<int>sta;
inline int add1(int x,int y){return x+y>=mod1?x+y-mod1:x+y;}
inline int add2(int x,int y){return x+y>=mod2?x+y-mod2:x+y;}
int main()
{
scanf("%s",ch+1);n=strlen(ch+1);
mp[make_pair(0,0)]=1ll;
for(int i=1;i<=n;++i)
{
if(sta.empty()||ch[sta.top()]!=ch[i])
{
sta.push(i);
hsh1[i]=add1(1ll*hsh1[i-1]*base1%mod1,ch[i]-‘a‘+1);
hsh2[i]=add2(1ll*hsh2[i-1]*base2%mod2,ch[i]-‘a‘+1);
}
else
{
sta.pop();
if(!sta.empty())hsh1[i]=hsh1[sta.top()],hsh2[i]=hsh2[sta.top()];
else hsh1[i]=hsh2[i]=0ll;
}
++mp[make_pair(hsh1[i],hsh2[i])];
}
for(map<pair<int,int>,ll>::iterator it=mp.begin();it!=mp.end();it++)
ans+=(it->second)*((it->second)-1ll)/2ll;
cout<<ans;
return 0;
}
先咕在这儿
标签:mes some 前缀 lin queue n+1 line return using
原文地址:https://www.cnblogs.com/zmyzmy/p/13763061.html