HIT暑期集训 2-SAT与后缀三连

后缀数组详解

后缀数组,倍增求sa与求height数组模板 UOJ模板题

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> 
#define maxn 100010
using namespace std;
int sa[maxn],rk[maxn],height[maxn],tmp[maxn],n,k;
char s[maxn];
bool cmp_sa(int i,int j)
{
    if (rk[i]!=rk[j]) return rk[i]<rk[j];
    int ri=(i+k<n)?rk[i+k]:-1;
    int rj=(j+k<n)?rk[j+k]:-1;
    return ri<rj;
} 
void get_sa()
{
    int i;
    for (i=0;i<=n;i++)
    {
        sa[i]=i;
        rk[i]=s[i];
    }
    for (k=1;k<=n;k<<=1)
    {
        sort(sa,sa+n,cmp_sa);
        tmp[sa[0]]=0;
        for (i=0;i<n;i++) tmp[sa[i+1]]=tmp[sa[i]]+(cmp_sa(sa[i],sa[i+1])?1:0);
        for (i=0;i<n;i++) rk[i]=tmp[i];
    }
}
void get_height()
{
    int i,j,k=0;
    for (i=0;i<n;i++) 
    {
        if (k) k--;
        if (rk[i]>=1) j=sa[rk[i]-1];
        while (s[i+k]==s[j+k] && i+k<n && j+k<n) k++;
        height[rk[i]]=k;
    }
}
int main()
{
    int i;
    scanf("%s",s);
    n=strlen(s);
    get_sa();
    get_height();
    for (i=0;i<n;i++) printf("%d ",sa[i]+1);
    printf("
"); 
    for (i=1;i<n;i++) printf("%d ",height[i]);
    return 0;
}
模板题 UOJ#35. 后缀排序

 2-SAT HDU-3062模板题

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define maxn 2005
#define maxm 1000005
using namespace std;
stack<int>st;
int num,last[maxn],dfn[maxn],low[maxn],vis[maxn],cnt;
int col[maxn],color;
struct edge
{
    int to,nxt;
}e[maxm<<1];
void clear()
{
    num=0;color=0;cnt=0;
    memset(last,-1,sizeof(last));
    memset(dfn,0,sizeof(dfn));
}
void add(int x,int y)
{
    e[++num].to=y;
    e[num].nxt=last[x];
    last[x]=num;
}
void tarjan(int x)
{
    int i,y,tp;
    dfn[x]=low[x]=++cnt;
    st.push(x);
    vis[x]=1;
    for (i=last[x];i!=-1;i=e[i].nxt)
    {
        y=e[i].to;
        if (!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if (vis[y])
        {
            low[x]=min(low[x],dfn[y]);
        }
    }
    if (low[x]==dfn[x])
    {
        color++;
        while (!st.empty())
        {
            tp=st.top();
            col[tp]=color;
            vis[tp]=0;
            st.pop();
            if (tp==x) break;
        }
    }
}
int main()
{
    int i,n,m,x,y,xid,yid,flag;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        clear();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&xid,&yid,&x,&y);
            x=x+(xid<<1);
            y=y+(yid<<1);
            add(x,y^1);
            add(y,x^1);
        }
        for(i=0;i<(n<<1);i++)
            if(!dfn[i])  tarjan(i);
        flag=0;
        for (i=0;i<n;i++)
            if (col[(i<<1)^1]==col[i<<1]) 
            {
                flag=1;
                break;
            }
        if (flag) printf("NO
");
        else printf("YES
");
    }
    return 0;
}
模板题 HDU-3062 Party

 2-SAT UVA1391模板题,注意用tarjan求解2-SAT时,col数组记录了拓扑排序的顺序

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define maxn 400010
#define maxm 400010
using namespace std;
stack<int>st;
int num,last[maxn],dfn[maxn],low[maxn],vis[maxn],cnt;
int col[maxn],color,a[maxn];
struct edge
{
    int to,nxt;
}e[maxm];
void clear()
{
    num=0;color=0;cnt=0;
    memset(last,-1,sizeof(last));
    memset(dfn,0,sizeof(dfn));
    memset(col,0,sizeof(col));
}
void add(int x,int y)
{
    e[++num].to=y;
    e[num].nxt=last[x];
    last[x]=num;
}
void tarjan(int x)
{
    int i,y,tp;
    dfn[x]=low[x]=++cnt;
    st.push(x);
    vis[x]=1;
    for (i=last[x];i!=-1;i=e[i].nxt)
    {
        y=e[i].to;
        if (!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if (vis[y])
        {
            low[x]=min(low[x],dfn[y]);
        }
    }
    if (low[x]==dfn[x])
    {
        color++;
        while (!st.empty())
        {
            tp=st.top();
            col[tp]=color;
            vis[tp]=0;
            st.pop();
            if (tp==x) break;
        }
    }
}
int main()
{
    int i,n,m,x,y,fx,fy,flag;
    double sum;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        if (n==0 && m==0) break;
        clear();
        sum=0;
        for (i=0;i<n;i++) 
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        sum=sum/n; 
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            x--;y--;
            fx=(a[x]<=sum);
            fy=(a[y]<=sum);
            if (fx==fy)
            {
                add((x<<1),(y<<1)|1);
                add((y<<1)|1,(x<<1));
                add((y<<1),(x<<1)|1);
                add((x<<1)|1,(y<<1));
            }  
            else 
            {
                add(x<<1|1,y<<1);
                add(y<<1|1,x<<1);
            }
        }
        for(i=0;i<(n<<1);i++)
            if(!dfn[i])  tarjan(i);
        flag=0;
        for(i=0;i<n;i++)
        {
            if (col[(i<<1)|1]==col[i<<1]) 
            {
                flag=1;
                break;
            }
        } 
        if (flag) printf("No solution.
");
        else 
        {
            for(i=0;i<n;i++)
            {
                if (col[(i<<1)|1]<col[i<<1]) printf("C
");
                else
                {
                    if (a[i]>=sum) printf("A
");
                    else printf("B
");
                }
            } 
        }
    }
    return 0;
}
模板题 UVA 1391 Astronauts

 A    POJ 3207

对于交错的连线[xi,yi]与[xj,yj],i在外则j在里,i在里则j在外,反过来也一样。

按照这点做2-SAT就可以了,(然而我没看懂题目),算是模板题。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define maxn 2005
#define maxm 1000005
using namespace std;
stack<int>st;
int num,last[maxn],dfn[maxn],low[maxn],vis[maxn],cnt;
int x[maxn],y[maxn],col[maxn],color;
struct edge
{
    int to,nxt;
}e[maxm];
void clear()
{
    num=0;color=0;cnt=0;
    memset(last,-1,sizeof(last));
    memset(dfn,0,sizeof(dfn));
}
void add(int x,int y)
{
    e[++num].to=y;
    e[num].nxt=last[x];
    last[x]=num;
}
void tarjan(int x)
{
    int i,y,tp;
    dfn[x]=low[x]=++cnt;
    st.push(x);
    vis[x]=1;
    for (i=last[x];i!=-1;i=e[i].nxt)
    {
        y=e[i].to;
        if (!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if (vis[y])
        {
            low[x]=min(low[x],dfn[y]);
        }
    }
    if (low[x]==dfn[x])
    {
        color++;
        while (!st.empty())
        {
            tp=st.top();
            col[tp]=color;
            vis[tp]=0;
            st.pop();
            if (tp==x) break;
        }
    }
}
int main()
{
    int i,j,n,m,flag;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        clear();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            if (x[i]>y[i]) swap(x[i],y[i]);
        }
        for (i=1;i<=m;i++)
        for (j=i+1;j<=m;j++)
            if ((x[i]<=x[j] && x[j]<=y[i] && y[i]<=y[j]) || (x[i]>=x[j] && x[j]>=y[i] && y[i]>=y[j]))
            {
                add((i<<1),(j<<1)|1);
                add((i<<1)|1,(j<<1));
                add((j<<1),(i<<1)|1);
                add((j<<1)|1,(i<<1));
            }
        for(i=0;i<(n<<1);i++)
            if(!dfn[i])  tarjan(i);
        flag=0;
        for (i=0;i<n;i++)
            if (col[(i<<1)^1]==col[i<<1]) 
            {
                flag=1;
                break;
            }
        if (flag) printf("the evil panda is lying again
");
        else printf("panda is telling the truth...
");
    }
    return 0;
}
View Code

E    UVA 11107

G    LibreOJ 2033

H    POJ 1743

思路:首先想到用差分来判断题目要求的重复旋律,接着只要找出差分数组中出现了两次的不重叠子串即可。

但是要注意一点,例如一串旋律为1 2 4 5 7,差分数组就是1 2 1 2,那么就可找出的字串1 2,但事实上124与467是重叠的。

二分子串长度x,每次二分判断就循环找到height不小于x的连续的i,记录sa[i]的最大值ma与最小值mi,通过判断ma-mi>=x判断两子串没有重叠。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> 
#define maxn 20010
using namespace std;
int sa[maxn],rk[maxn],height[maxn],tmp[maxn],n,k;
char s[maxn],a[maxn];
bool cmp_sa(int i,int j)
{
    if (rk[i]!=rk[j]) return rk[i]<rk[j];
    int ri=(i+k<n)?rk[i+k]:-1;
    int rj=(j+k<n)?rk[j+k]:-1;
    return ri<rj;
} 
void get_sa()
{
    int i;
    for (i=0;i<=n;i++)
    {
        sa[i]=i;
        rk[i]=s[i];
    }
    for (k=1;k<=n;k<<=1)
    {
        sort(sa,sa+n,cmp_sa);
        tmp[sa[0]]=0;
        for (i=0;i<n;i++) tmp[sa[i+1]]=tmp[sa[i]]+(cmp_sa(sa[i],sa[i+1])?1:0);
        for (i=0;i<n;i++) rk[i]=tmp[i];
    }
}
void get_height()
{
    int i,j,k=0;
    for (i=0;i<n;i++) 
    {
        if (k) k--;
        if (rk[i]>=1) j=sa[rk[i]-1];
        while (s[i+k]==s[j+k] && i+k<n && j+k<n) k++;
        height[rk[i]]=k;
    }
}
int check(int x)
{
    int ma=sa[1],mi=sa[1],i;
    for (i=2;i<=n;i++)
    {
        if (height[i]>=x && i<n)
        {
            ma=max(ma,sa[i]);
            mi=min(mi,sa[i]);
        }
        else 
        {
            if (ma-mi>=x) return 1;//判断两段旋律不会重合 
            ma=sa[i];mi=sa[i];
        }
    }
    return 0;
}
int query(int l,int r)
{
    int mid;
    while (l<r)
    {
        mid=(l+r+1)>>1;
        if (check(mid)) l=mid;
        else r=mid-1;
    }
    l++;
    if (l>=5) return l;
    return 0;
}
int main()
{
    int i;
    while (scanf("%d",&n)!=EOF && n!=0)
    {
        for (i=0;i<n;i++) scanf("%d",&a[i]);
        n--;
        for (i=0;i<n;i++) s[i]=a[i+1]-a[i]; 
        get_sa();
        get_height();
        printf("%d
",query(0,n));
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lsykk/p/13606037.html