2019 Multi-University Training Contest 2

 

 

 

Beauty Of Unimodal Sequence

题意:给一个长度为n的数组,让你从中选出任意一组长度最长的单峰序列,要求输出这个单峰序列下标的最大字典序和最小字典序。

思路:贪心,对于字典序最小的下标,先正反跑LIS,求出第一个峰值,可知这个峰值对应的数字必选,因为已满足题意,且靠前。然后继续贪心选择这个位置前面满足条件的值字典序最小。

对于字典序最大的下标,则需要找到最后一个峰值。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int maxn=3e5+10;
int box[maxn];
int ans1[maxn],ans2[maxn];
int len1[maxn],len2[maxn]; // 最长上升,下降序列
int num[maxn];
int a[maxn];

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        box[1]=-1;
        for(int i=1; i<=n; i++)
            box[i]=0;
        for(int i=1; i<=n; i++)
            ans1[i]=ans2[i]=0;
        int cnt=0;
        for(int i=1; i<=n; i++)
        {
            if(a[i]>box[cnt])
            {
                box[++cnt]=a[i];
                len1[i]=cnt;
            }
            else
            {
                len1[i]=lower_bound(box+1,box+1+cnt,a[i])-box;
                box[len1[i]]=a[i];
            }
        }
        for(int i=1; i<=n; i++)
            box[i]=0;
        cnt=0;
        for(int i=n; i>=1; i--)
        {
            if(a[i]>box[cnt])
            {
                box[++cnt]=a[i];
                len2[i]=cnt;
            }
            else
            {
                len2[i]=lower_bound(box+1,box+1+cnt,a[i])-box;
                box[len2[i]]=a[i];
            }
        }
        int maxlen=0,maxid;
        for(int i=1; i<=n; i++)
        {
            if((len1[i]+len2[i]-1)>maxlen)
            {
                maxlen=len1[i]+len2[i]-1;
                maxid=i;
            }
        }
        ans1[len1[maxid]]=maxid;
//        printf("%d?\n",maxid);
//    printf("%d %d?\n",a[maxid-1],a[ans1[len1[maxid-1]+1]]);
        for(int i=maxid-1; i>=1; i--)
        {
            if(a[i]<a[ans1[len1[i]+1]])
                ans1[len1[i]]=i;
        }
        for(int i=maxid+1; i<=n; i++)
        {
            if(ans1[maxlen-len2[i]+1]==0&&a[i]<a[ans1[maxlen-len2[i]]])
                ans1[maxlen-len2[i]+1]=i;
        }
        for(int i=1; i<=maxlen; i++)
        {
            if(i!=maxlen) printf("%d ",ans1[i]);
            else printf("%d",ans1[i]);
        }
        printf("\n");


        maxlen=0;
        for(int i=n; i>=1; i--)
        {
            if((len1[i]+len2[i]-1)>maxlen)
            {
                maxlen=len1[i]+len2[i]-1;
                maxid=i;
            }
        }
        ans2[len1[maxid]]=maxid;
        for(int i=maxid-1; i>=1; i--)
        {
            if(a[i]<a[ans2[len1[i]+1]]&&ans2[len1[i]]==0)
                ans2[len1[i]]=i;
        }
        for(int i=maxid+1; i<=n; i++)
        {
            if(a[i]<a[ans2[maxlen-len2[i]]])
                ans2[maxlen-len2[i]+1]=i;
        }
        for(int i=1; i<=maxlen; i++)
        {
            if(i!=maxlen) printf("%d ",ans2[i]);
            else printf("%d",ans2[i]);
        }
        printf("\n");
    }
}
View Code

 

 

Longest Subarray

题意:给n,c,k,给n个数字,已知数字都小于c,求一个合法连续数字串的最长长度,合法串要求出现的数字要么出现0次要么就必须出现次数>=k。

思路:从左到右枚举区间右端点,与此同时用线段树维护满足条件的左端点,线段树每个叶节点代表这个节点作为左端点时,所拥有的满足条件的数字个数(如果为C证明这个左端点可取)。从左到右枚举右端点时,判断每个新增的数字,

这个数字对前面的每个左端点拥有的合法数字个数有两点影响:

1.设目前右端点枚举到第i位,这个数字上一次出现的位置为pos,那么在[pos+1,i-1]这个区间内的点作为左端点时,i-1作为右端点时,是没有a[i]这个数字,而i作为右端点出现了a[i],所以[pos+1,i-1]合法个数需要-1.

2.设目前右端点枚举到第i位,这个数字这个数字目前出现记为第一次,往前的第k+1位的位置记为pos,第k位位置记为pos2,需要注意,在[pos+1,pos2]这个区间作为左端点,i-1作为右端点时,a[i]出现了K-1次,而枚举到右端点为i,新增了a[i]后[pos+1,i]这个区间中a[i]出现次数达到k次了,区间内的合法个数需要+1.

对于每次枚举一个右端点,线段树中叶子节点为C的位置就是合法的,[1,i]线性搜索太慢,所以需要线段树记录叶子节点的最大值和最大值的位置,在query时优先查询靠左边的同时满足最大值为C的位置。用vector记录每个数字出现的位置。

 

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
vector<int> v[maxn];
int n,k,c;
struct note
{
    int left,right,maxx,lazy,ans;
    void up(int val)
    {
        maxx+=val;
        lazy+=val;
    }
} tree[maxn*4];
void pushup(int id)
{
    tree[id].maxx=max(tree[id<<1].maxx,tree[id<<1|1].maxx);
    if(tree[id].maxx==tree[id<<1].maxx) tree[id].ans=tree[id<<1].ans;
    else tree[id].ans=tree[id<<1|1].ans;
}
void pushdown(int id)
{
    if(tree[id].lazy)
    {
        tree[id<<1].up(tree[id].lazy);
        tree[id<<1|1].up(tree[id].lazy);
        tree[id].lazy=0;
    }
}

void build(int id,int l,int r)
{
    tree[id].left=tree[id].ans=l;
    tree[id].right=r;
    tree[id].maxx=tree[id].lazy=0;
    if(l==r)
        return;
    int mid=(l+r)/2;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id);
}
int query(int id,int l,int r)
{
    if(tree[id].maxx!=c)
        return -1;
    if(l<=tree[id].left&&tree[id].right<=r)
        return tree[id].ans;
    pushdown(id);
    int mid=(tree[id].left+tree[id].right)/2;
    if(r<=mid) return query(id<<1,l,r);
    else if(l>mid) return query(id<<1|1,l,r);
    else
    {
        int tmp;
        tmp=query(id<<1,l,mid);
        if(tmp!=-1) return tmp;
        else return query(id<<1|1,mid+1,r);
    }
}
void update(int id,int l,int r,int val)
{
    if(l<=tree[id].left&&tree[id].right<=r)
    {
        tree[id].up(val);
        return;
    }
    pushdown(id);
    int mid=(tree[id].left+tree[id].right)/2;
    if(l<=mid) update(id<<1,l,r,val);
    if(r>mid) update(id<<1|1,l,r,val);
    pushup(id);
}


int main()
{
    while(~scanf("%d%d%d",&n,&c,&k))
    {
        for(int i=1; i<=c; i++)
        {
            v[i].clear();
            v[i].push_back(0);
        }
        build(1,1,n);
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            int x;
            scanf("%d",&x);
            update(1,i,i,c-1);
            if(v[x].back()<i-1) update(1,v[x].back()+1,i-1,-1);
            v[x].push_back(i);
            if(v[x].size()>=k+1)
            {
                int pos=v[x].size()-k-1;
                update(1,v[x][pos]+1,v[x][pos+1],1);
            }
            int tmp=query(1,1,i);
            if(tmp!=-1)
                ans=max(ans,i-tmp+1);
        }
        printf("%d\n",ans);
    }
}
View Code

 

 

I Love Palindrome String  回文自动机,哈希

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
const int maxn = 300005 ;
const int N = 26 ;
int nxt[maxn][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[maxn] ;
int num[maxn] ;
int len[maxn] ;//len[i]表示节点i表示的回文串的长度
int S[maxn] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
char a[maxn];
int id[maxn];
const ull hash1=201320111;
ull ha[maxn],pp[maxn];
int ans[maxn];
ull getha(int l,int r)
{
    if(l==1) return ha[r];
    else return ha[r]-ha[l-1]*pp[r-l+1];
}
int check(int l,int r)
{
    int mid=(l+r)/2;
    if((r-l+1)%2==0) return getha(l,mid)==getha(mid+1,r);
    return getha(l,mid)==getha(mid,r);

}
int newnode ( int l )  //新建节点
{
    for ( int i = 0 ; i < N ; ++ i ) nxt[p][i] = 0 ;
    cnt[p] = 0 ;
    num[p] = 0 ;
    len[p] = l ;
    return p ++ ;
}

void init ()  //初始化
{
    p = 0 ;
    newnode (  0 ) ;
    newnode ( -1 ) ;
    last = 0 ;
    n = 0 ;
    S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
    fail[0] = 1 ;
}

int get_fail ( int x )  //和KMP一样,失配后找一个尽量最长的
{
    while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
    return x ;
}

void add (int c )
{
    c -= 'a' ;
    S[++ n] = c ;
    int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
    if ( !nxt[cur][c] )  //如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
    {
        int now = newnode ( len[cur] + 2 ) ;//新建节点
        fail[now] = nxt[get_fail (fail[cur])][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
        nxt[cur][c] = now ;
        num[now] = num[fail[now]] + 1 ;
    }
    last = nxt[cur][c] ;
    cnt[last] ++ ;
    id[last]=n;
}


void count ()
{
    for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
    //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    for(int i=2; i<p; i++)
    {
        if(check(id[i]-len[i]+1,id[i]))
            ans[len[i]]+=cnt[i];
//        for(int j=id[i]-len[i]+1;j<=id[i];j++)
//            printf("%c",a[j]);
//        printf(" %d \n",cnt[i]);
    }
}
int main()
{
    pp[0]=1;
    for(int i=1; i<maxn; i++)
        pp[i]=pp[i-1]*hash1;
    while(~scanf("%s",a+1))
    {
        int len1=strlen(a+1);
        for(int i=1; i<=len1; i++)
            ans[i]=0;
        ha[0]=0;
        for(int i=1; i<=len1; i++)
            ha[i]=ha[i-1]*hash1+a[i];
//        printf("%d",check(1,7));
        init();
        for(int i=1; i<=len1; i++)
            add(a[i]);
        count();
        for(int i=1; i<=len1; i++)
        {
            if(i!=n) printf("%d ",ans[i]);
            else  printf("%d",ans[i]);
        }
        printf("\n");
    }
}
View Code

 

 

 

Keen On Everything But Triangle

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll ;
const int oo=0x7f7f7f7f ;
const int maxn=4e5+7;
const int mod=1e9+7;
int n,m,cnt,root[maxn],a[maxn],x,y,k;
struct node
{
    int l,r,sum;
} T[maxn*25];
vector<int> v;
int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void update(int l,int r,int &x,int y,int pos)
{
    T[++cnt]=T[y],T[cnt].sum++,x=cnt;
    if(l==r) return;
    int mid=(l+r)/2;
    if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos);
    else update(mid+1,r,T[x].r,T[y].r,pos);
}
int query(int l,int r,int x,int y,int k)
{
    if(l==r) return l;
    int mid=(l+r)/2;
    int sum=T[T[y].l].sum-T[T[x].l].sum;
    if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);
    else return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
int main(void)
{
    while(~scanf("%d%d",&n,&m))
    {
        v.clear();
        for(int i=1; i<=n; i++) scanf("%d",&a[i]),v.push_back(a[i]);
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        for(int i=1; i<=n; i++) update(1,n,root[i],root[i-1],getid(a[i]));
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);
            ll ans=-1;
            int len=y-x+1;
            if(len>=3)
            {
                ll x1,x2,x3;
                x1=(ll)v[query(1,n,root[x-1],root[y],len)-1];
                x2=(ll)v[query(1,n,root[x-1],root[y],len-1)-1];
                x3=(ll)v[query(1,n,root[x-1],root[y],len-2)-1];
                if(x1<x2+x3)
                    ans=(ll)x1+x2+x3;
                else
                {
                    for(int j=len-3; j>=1; j--)
                    {
                        x1=x2;
                        x2=x3;
                        x3=(ll)v[query(1,n,root[x-1],root[y],j)-1];
                        if(x1<x2+x3)
                        {
                            ans=(ll)x1+x2+x3;
                            break;
                        }
                    }
                }
            }
            if(ans==-1) printf("-1\n");
            else printf("%lld\n",ans);

        }
        for(int i=1; i<=n; i++)
            root[i]=0;
        for(int i=1; i<=25*n; i++)
            T[i].l=0,T[i].r=0,T[i].sum=0;
    }
    return 0;
}
View Code

 

 

posted @ 2019-09-25 21:32  paranoid。  阅读(181)  评论(0编辑  收藏  举报