E:
传送门
题意:
给
n
个由
题解:
首先考虑怎么满足一个串比后面所有的都要小?是一个一个判断,还是有别的方法。
其实只要保证该串小等于后面的一个串就好了。
下面考虑怎么满足小等于后面的一个串。
设前面的串为 s1 ,后面的串为 s2 。
如果
s1
是
s2
的前缀,那么显然无论如何改变大小写都无法使得这两个串大小关系改变,直接跳过。
同理若
s2
是
s1
的前缀,那么直接输出”No”。
不然设两串第一个不同的位置为
k
。考虑两种不同的情况。
1.
2.
s1[k]>s2[k]
,此时
s1[k]
必定是大写,
s2[k]
必定是小写。
发现上面的思想就是 2−SAT 思想,用 Tarjan 方法可以 O(n) 求解。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
struct IO{
inline int read(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
inline void W(int x){
static int buf[50];
if(!x){putchar('0');return;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10;x/=10;}
while(buf[0])putchar(buf[buf[0]--]+'0');
}
}io;
const int Maxn=1e5+50;
int n,m,dfn[Maxn<<1],low[Maxn<<1],st[Maxn<<1],top,tot,id[Maxn<<1],ind,ins[Maxn<<1];
vector<int>ch[Maxn];
vector<int>edge[Maxn<<1];
#define is(x) 2*x-1
#define isnot(x) 2*x
#define addedge(x,y) edge[x].push_back(y)
inline void dfs(int now){
dfn[now]=low[now]=++ind;st[++top]=now;ins[now]=1;
for(int e=edge[now].size()-1;e>=0;e--){
int v=edge[now][e];
if(dfn[v]){
if(!ins[v])continue;
low[now]=min(low[now],dfn[v]);
}
else dfs(v),low[now]=min(low[now],low[v]);
}
if(low[now]==dfn[now]){
++tot;
while(low[st[top]]==dfn[now]){
int u=st[top--];
id[u]=tot;ins[u]=0;
}
}
}
int main(){
n=io.read(),m=io.read();
for(int i=1;i<=n;i++){
int l=io.read();
for(int j=1;j<=l;j++){
ch[i].push_back(io.read());
}
}
for(int i=2;i<=n;i++){
int l1=ch[i-1].size(),l2=ch[i].size(),lim=min(l1,l2),pos=0;
while(pos<lim&&ch[i-1][pos]==ch[i][pos])pos++;
if(pos==lim){
if(l1>l2){
puts("No");return 0;
}
}else{
if(ch[i-1][pos]<ch[i][pos]){
addedge(is(ch[i-1][pos]),is(ch[i][pos]));
addedge(isnot(ch[i][pos]),isnot(ch[i-1][pos]));
}else{
addedge(isnot(ch[i][pos]),is(ch[i][pos]));
addedge(is(ch[i-1][pos]),isnot(ch[i-1][pos]));
}
}
}
for(int i=1;i<=m;i++){
if(!dfn[is(i)])dfs(is(i));
if(!dfn[isnot(i)])dfs(isnot(i));
}
static vector<int>ans;
for(int i=1;i<=m;i++){
if(id[is(i)]>id[isnot(i)])ans.push_back(i);
else if(id[is(i)]==id[isnot(i)]){
puts("No");return 0;
}
}
puts("Yes");
printf("%d\n",(int)ans.size());
for(int e=0;e<ans.size();e++){
printf("%d ",ans[e]);
}
}
F:
传送门
题意:
求满足区间或大于区间最大值的区间个数。
题解:
考虑一个区间,只要这个区间的任意值非最大值有一位不与最大值相同,那么这个区间就是合法区间。
考虑找出所有值左右第一个大于它的位置,那么以它为最大值的区间就固定在这一段中。只要再找出这个区间中左右第一个有一位不与最大值相同的值的位置,那么这个位置左边的所有位置都可以与最大值右边的位置构成一个合法区间。右边也同理。
这样计算会有重复的部分,只需要减去两端算重的部分即可。
现在问题转化为找出每个值左右比他大的第一个位置,单调栈 O(n) 可以完成。
同时,要求出左右第一个与最大值不同的位置,只需要 O(nlogn) 求即可。
还有一种特殊的情况 {3,3,3,3} ,即相等的情况。考虑怎么去重。
其实只需要保证每个区间的最大值是最右边的那个值就行了。具体实现就是单调栈扫描一边满足 <ai ,一边满足 ≤ai .
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
struct IO{
inline int read(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
inline void W(int x){
static int buf[50];
if(!x){putchar('0');return;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10;x/=10;}
while(buf[0])putchar(buf[buf[0]--]+'0');
}
}io;
const int Maxn=2e5+50;
int n,a[Maxn],l[Maxn],r[Maxn],st[Maxn],pos[Maxn],top,diff_l[Maxn],diff_r[Maxn],mx[35];
int mxpos[35];
int main(){
n=io.read();
for(int i=1;i<=n;i++)a[i]=io.read();
for(int i=1;i<=n;i++){
while(top&&st[top]<=a[i])top--;
l[i]=pos[top]+1;
st[++top]=a[i];pos[top]=i;
}
top=0;pos[top]=n+1;
for(int i=n;i>=1;i--){
while(top&&st[top]<a[i])top--;
r[i]=pos[top]-1;
st[++top]=a[i];pos[top]=i;
}
for(int i=1;i<=n;i++){
int p=0;
for(int j=0;(1ll<<j)<=a[i];j++){
if((1ll<<j)&a[i])mx[j]=max(mx[j],i);
else p=max(p,mx[j]);
}
diff_l[i]=p;
}
fill(mx,mx+32+1,n+1);
for(int i=n;i>=1;i--){
int p=n+1;
for(int j=0;(1ll<<j)<=a[i];j++){
if((1ll<<j)&a[i])mx[j]=min(mx[j],i);
else p=min(p,mx[j]);
}
diff_r[i]=p;
}
static long long ans=0;
for(int i=1;i<=n;i++){
if(diff_l[i]>=l[i]){
ans+=1ll*(diff_l[i]-l[i]+1)*(r[i]-i+1);
}
if(diff_r[i]<=r[i]){
ans+=1ll*(r[i]-diff_r[i]+1)*(i-l[i]+1);
}
if(diff_l[i]>=l[i]&&diff_r[i]<=r[i]){
ans-=1ll*(r[i]-diff_r[i]+1)*(diff_l[i]-l[i]+1);
}
}
cout<<ans<<endl;
}