Codeforces Round #441 Div. 2 E,F

来源:互联网 发布:linux服务器配置ipv6 编辑:程序博客网 时间:2024/05/22 06:56

E:
传送门

题意:
n个由1~ m数字组成的字符串(n,m1e5),每个字符串长度为li(ili1e5)。字典序大小满足小写比大写都大。现在可以改变任意数字的大小写,满足所有前面的串字典序小等于后面的串。

题解:
首先考虑怎么满足一个串比后面所有的都要小?是一个一个判断,还是有别的方法。

其实只要保证该串小等于后面的一个串就好了。

下面考虑怎么满足小等于后面的一个串。

设前面的串为s1,后面的串为s2

如果s1s2的前缀,那么显然无论如何改变大小写都无法使得这两个串大小关系改变,直接跳过。
同理若s2s1的前缀,那么直接输出”No”。

不然设两串第一个不同的位置为k。考虑两种不同的情况。
1.s1[k]<s2[k],显然此时若s2[k]是大写,那么s1[k]是大写,s1[k]是大写,那么s2[k]是小写。
2.s1[k]>s2[k],此时s1[k]必定是大写,s2[k]必定是小写。

发现上面的思想就是2SAT思想,用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;}
原创粉丝点击