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

 

 

转载于:https://www.cnblogs.com/dongdong25800/p/11575187.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值