2017 ACM/ICPC Asia Regional Shenyang Online

2017 ACM/ICPC Asia Regional Shenyang Online

写一些题解,完全不想总结。唯一的感想就是开黑打都被虐。。主账号被我智障似的T了一发,lzq手误把一份AC代码交了两个账号。。A题卡太久。。。

听某化工大教练说我校rank 87,应该有名额吧。。

本场我写了两个题外加一个助攻,终榜过了6道题,堪忧。

按题号来看看这6个题以及刚补的1010吧:


1001 string string string

 开场三秒给队友说了题意,求有多少个字串恰好出现k次。后缀数组基本忘完了,交给他们了。AC时间:03:51

献上大佬代码:

#include<bits/stdc++.h>
using namespace  std;
#define LL long long
using namespace std;
const int N=2e5+10;
int x[N*3];
int AC[N*3],AB[N*3],ABC[N*3],AD[N*3];
int NC(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void DA(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=AC,*y=AB,*t;
    for(i=0; i<m; i++) ABC[i]=0;
    for(i=0; i<n; i++) ABC[x[i]=r[i]]++;
    for(i=1; i<m; i++) ABC[i]+=ABC[i-1];
    for(i=n-1; i>=0; i--) sa[--ABC[x[i]]]=i;
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) AD[i]=x[y[i]];
        for(i=0; i<m; i++) ABC[i]=0;
        for(i=0; i<n; i++) ABC[AD[i]]++;
        for(i=1; i<m; i++) ABC[i]+=ABC[i-1];
        for(i=n-1; i>=0; i--) sa[--ABC[AD[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=NC(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return ;
}
int h[N*3];
int Rank[N*3];
void get_height(int *r,int *sa,int n)
{
    int k=0,j;
    for(int i=1; i<=n; i++) Rank[sa[i]]=i;
    for(int i=0; i<n; h[Rank[i++]]=k)
        for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++) ;
    return ;
}
int AS[N*3],r[N*3];
LL s[N][2];
char str[N];
struct node
{
    int h,d,mi;
    node(int _h,int _d,int _mi)
    {
        h=_h,mi=_mi,d=_d;
    }
};
int AAC(int n,int k)
{

    stack<node>zh;
    zh.push(node(0,1,0));
    LL res=0;
    h[n+1]=0;
    for(int i=2; i<=n+1; i++)
    {
        int x=h[i];
        if(x>zh.top().h) zh.push(node(x,1,x));
        else
        {
            int ti=0;
            node now(x,1,x);
            while(zh.top().h>x)
            {
                int tmp=zh.top().h;
                now.d+=zh.top().d;
                now.mi=min(now.mi,zh.top().mi);
                ti+=zh.top().d;
                zh.pop();

                if(ti==k-1)
                {
                    int delta=max(x,zh.top().h);
                    res+=tmp-max(x,zh.top().h);
                }
            }
            zh.push(now);
        }
    }
    printf("%I64d\n",res);
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        int k;
        scanf("%d%s",&k,str);
        memset(s,0,sizeof(s));
        memset(h,0,sizeof(h));
        int len=strlen(str);
        for(int i=0; i<len; i++)  x[i]=str[i];
        x[len]=0;
        DA(x,AS,len+1,255);
        get_height(x,AS,len);
        if(k==1)
        {
            LL ans=0;
            for(int i=1; i<=len; i++)
            {
                int tmp=len-AS[i];
                ans+=tmp-max(h[i],h[i+1]);
            }
            printf("%I64d\n",ans);
        }
        else AAC(len,k);
    }
    return 0;
}



1002 cable cable cable

 我们第二道出的题,然后被题意卡了很久,lzq大队长首先发现是个shab题,一发过了,然而都已经过了200+个队了。AC时间:0:25

再次献上大佬代码:

    ll n,m;
   while(~scanf("%lld%lld",&n,&m))
   {
       printf("%lld\n",(n-m+1)*m);
   }



1004 array array array

 哈哈,终于轮到我出的题了。在此感谢北化工教练带我们联合训练,正好前几天做过一个原题,具体移驾: 2015 ICPC长春-HDU5532

从后往前看题,看懂这题后瞬间明白好像是原题,然后刷了一下榜,果然CQU率先AC,然后跟杨神说是原题,结果换来一张懵逼脸,直接上,把原题的1改成k-AC。0:08

const int N=1e6+10;
int a[N],b[N];
char s[N];
int main()
{
    int t,n,kk;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&kk);
        for(int i=0; i<n; i++) scanf("%d",&a[i]);
        if(n<=2) puts("A is a magic array.");
        else
        {
            memset(b,0,sizeof(b));
            int len=0,f=0;
            b[len++]=a[0];
            for(int i=1; i<n; i++)
            {
                if(a[i]>=b[len-1]) b[len++]=a[i];
                else
                {
                    int pos=upper_bound(b,b+len,a[i])-b;
                    b[pos]=a[i];
                }
            }
            if(len>=n-kk) f=1;
            len=0;
            b[len++]=a[n-1];
            for(int i=n-2; i>=0&&!f; i--)
            {
                if(a[i]>=b[len-1]) b[len++]=a[i];
                else
                {
                    int pos=upper_bound(b,b+len,a[i])-b;
                    b[pos]=a[i];
                }
            }
            if(len>=n-kk) f=1;
            if(f) puts("A is a magic array.");
            else puts("A is not a magic array.");
        }

    }
    return 0;
}


1005 number number number

本校第4道过的题,学姐太强了。貌似一个递推式然后裸矩阵快速幂,没有去补。

献上代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define lson l,m,root<<1
#define rson m+1,r,root<<1|1
typedef long long ll;
using namespace std;
const int MOD=998244353;
const int maxn=2000010;
struct NODE
{
    ll m[2][2];
};
NODE CSAL(NODE A,NODE B)
{
    NODE C;
    memset(C.m,0,sizeof(C.m));
    for(int i=0; i<2; i++)
    {
        for(int j=0; j<2; j++)
        {
            for(int k=0; k<2; k++)
            {
                C.m[i][j]=C.m[i][j]+A.m[i][k]*B.m[k][j];
                C.m[i][j]%=MOD;
            }
        }
    }
    return C;
}
NODE KSM(NODE A,ll p)
{
    NODE tmp;
    memset(tmp.m,0,sizeof(tmp.m));
    for(int i=0; i<2; i++)
        tmp.m[i][i]=1;
    while(p)
    {
        if(p&1)
            tmp=CSAL(tmp,A);
        A=CSAL(A,A);
        p>>=1;
    }
    return tmp;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        NODE tmp,res,TC;
        tmp.m[0][0]=1,tmp.m[0][1]=1;
        tmp.m[1][0]=1, tmp.m[1][1]=0;
        res=KSM(tmp,3+2*n);
        TC.m[0][0]=0,TC.m[0][1]=1;
        TC.m[1][0]=0,TC.m[1][1]=0;
        res=CSAL(TC,res);
        cout<<(res.m[0][0]+MOD-1)%MOD<<endl;
    }
}


1008 transaction transaction transaction

 再次感谢杨神读题,然后给出了思路,求最长路即可,讨论了一下,把点权转化成边权,构造一个出发点0点和各点间路径长为0,两点间路径虽然是双向的,但费用的计算却要反过来,也就是存在负权边,不能用dij求解。dijstra+priority_queue写习惯了,然后一发板子发现样例过不去,发现vis[]过的点不能再次访问,于是改成spfa样例过了,一发结果T了, 发现数组少了个0,石乐志。AC时间:0:57
const int N=200000+10;
struct Edge
{
    int u,to,next;
    ll w;
} e[N*2];
int n,m,tot;
int vis[N],head[N],num[N];
ll d[N],ans,tc[N];
struct node
{
    ll c;
    int v;
    friend bool operator < (node a,node b)
    {
        return a.c<b.c;
    }
};
//priority_queue<node>q;
void init()
{
    tot=0;
//    while(!q.empty()) q.pop();
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
}
void add(int u,int v,ll w)
{
    e[tot].to=v,e[tot].w=w,e[tot].next=head[u];
    head[u]=tot++;
}
void dij()
{
    int f=0;
    d[0]=ans=0;
    for(int i=1; i<=n; i++) d[i]=-1;
    queue<int>q;
    q.push(0);
    while(!q.empty()&&!f)
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i+1;i=e[i].next)
        {
            int v=e[i].to;
            ll w=e[i].w;
            if(d[v]<d[u]+w)
            {
                d[v]=d[u]+w;
//                printf("%d %lld\n",v,d[v]);
                ans=max(ans,d[v]);
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
//    q.push(node {d[0],0});
//    while(!q.empty())
//    {
//        node tmp=q.top();
//        q.pop();
//        int u=tmp.v;
//        if(vis[u]) continue;
//        vis[u]=1;
//        for(int i=head[u]; i+1; i=e[i].next)
//        {
//            int v=e[i].to;
//            ll w=e[i].w;
//            if(d[v]<d[u]+w)
//            {
//                d[v]=d[u]+w;
//                ans=max(ans,d[v]);
                printf("%d %lld\n",v,d[v]);
//                q.push(node {d[v],v});
//            }
//        }
//    }
    printf("%lld\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&tc[i]);
            add(0,i,0);
        }
        for(int i=1; i<n; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,tc[v]-tc[u]-w);
            add(v,u,tc[u]-tc[v]-w);
        }
        dij();
    }
}
四个题,发现排名在150左右。于是转攻最后一题,01题队友一直在讨论,string不是我们的强项。




1010 ping ping ping(补)

 做完第五题只差队友在写的01了,还剩两三个小时左右,此时06和10是最可能做的,06过的人多,讨论了一下完全没想法。杨神给出这道题的题意,树上操作,很有意思。然而我们一堆人陷在最小覆盖和匹配问题上无法自拔。

题意:给你一棵树,然后q条链,每条链由两个点表示这条链不连通,求树上最少坏了几个点。

为什么会想到最小覆盖上去:这很好理解,用最少的点覆盖最多的路径,然而这是路径并非边。学姐给出了把链转化成区间然后贪心思想求最少的点覆盖所有区间,貌似思路不错,但树上任意两个点并非是一个连续区间,树剖也只能将其分成若干连续区间。06和10兵分两路,我选择了10,卡了很久。4个小时左右01过了,真是辛苦了三位string大佬。然后跟他们说了06和10的题意,也就是把之前想到过的思路重新走了一下,队友zp在4:30左右想到tarjan思想,断成联通块,从叶节点往上贪心,然后我突然想到,两条链的交点必定是这两条链的公共祖先的某一个,于是把想法跟他们说了一下,我很确信这是正确的。zp喊我去说了一下他的思路,然而交流困难,并没有懂他的想法,但大佬觉得很对,于是他干脆自己写。距离比赛结束还有40分钟左右,挺玄的,06题学弟和队友sb在写,然而学弟的代码T了,突然发现09题怎么提交人数这么多,问了一下题意,no sponsor,又发现T<10,status里面好多人都在交09。于是打算最后再试试吧。这题最终还是没做出来,晚上看了一下别人的想法,大概就是我的想法和zp的贪心策略结合一下。

因为任意两条链的交点必定是其中某条的LCA,所以答案必定在这Q个LCA里面选最少个,从叶节点往上相当于将所有lca按深度从大到小排序。但这个dfs序建线段树我是没想到,不过队友还是能想到。今天看了一下题解,原来也不难。每个点的dfs序分为in和out,然后如果某条链的两个节点的dfs序都未被标记,那么ans++,将这条链的lca的in和out在线段树上表示的这段区间标记。遍历到某条链只需看其两个点的dfs序是否被标记过。貌似有点说不清,请参考:Link 

理解之后就很简单了。

const int N=1e5+10;
struct node
{
    int l,r,f;
} a[N<<2];
struct Edge
{
    int to,next;
} e[N];
struct LCA
{
    int u,v,father;
} ac[N];
int n,q,head[N],tot,ti,in[N],out[N],dep[N];
int fa[20][N],s[N],t[N];
void init()
{
    ti=tot=0;
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(head,-1,sizeof(head));
    memset(fa,-1,sizeof(fa));
}
int cmp(LCA A,LCA B)
{
    return dep[A.father]>dep[B.father];
}
void add(int u,int v)
{
    e[tot].to=v,e[tot].next=head[u];
    head[u]=tot++;
    e[tot].to=u,e[tot].next=head[v];
    head[v]=tot++;
}
void dfs(int u,int pre,int d)
{
    fa[0][u]=pre,dep[u]=d,in[u]=++ti;
    for(int i=head[u]; i+1; i=e[i].next)
    {
        int v=e[i].to;
        if(v==pre) continue;
        dfs(v,u,d+1);
    }
    out[u]=++ti;
}
void init_lca()
{
    fa[0][1]=-1;
    for(int k=0; k<17; k++)
        for(int i=1; i<=n; i++)
            if(fa[k][i]<0) fa[k+1][i]=-1;
            else fa[k+1][i]=fa[k][fa[k][i]];
}
int lca(int u,int v)
{
    if(dep[v]>dep[u]) swap(u,v);
    for(int k=0; k<17; k++)
        if((dep[u]-dep[v])>>k&1) u=fa[k][u];
    if(u==v) return u;
    for(int i=16; i>=0; i--)
        if(fa[i][u]!=fa[i][v])
        {
            u=fa[i][u];
            v=fa[i][v];
        }
    return fa[0][u];
}
void pushdown(int k)
{
    if(a[k].f&&a[k].l!=a[k].r)
    {
        a[k*2].f=a[k*2+1].f=1;
        a[k].f=0;
    }
}
void build(int l,int r,int k)
{
    a[k].l=l,a[k].r=r,a[k].f=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
}
void update(int l,int r,int k)
{
    if(l<=a[k].l&&a[k].r<=r)
    {
        a[k].f=1;
        return ;
    }
    pushdown(k);
    int mid=(a[k].l+a[k].r)/2;
    if(l<=mid) update(l,r,2*k);
    if(r>mid) update(l,r,2*k+1);
}
int query(int id,int k)
{
    if(a[k].l==a[k].r&&a[k].l==id) return a[k].f;
    pushdown(k);
    int mid=(a[k].l+a[k].r)/2;
    if(id<=mid) return query(id,2*k);
    else return query(id,2*k+1);
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        for(int i=1; i<=n; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u+1,v+1);
        }
        n++;
        dfs(1,1,1);
        init_lca();
        build(1,ti,1);
        scanf("%d",&q);
        for(int i=0; i<q; i++)
        {
            scanf("%d%d",&ac[i].u,&ac[i].v);
            ac[i].u++,ac[i].v++;
            ac[i].father=lca(ac[i].u,ac[i].v);
        }
        sort(ac,ac+q,cmp);
        int ans=0;
        for(int i=0; i<q; i++)
            if(!query(in[ac[i].u],1)&&!query(out[ac[i].v],1))
            {
                ans++;
                update(in[ac[i].father],out[ac[i].father],1);
            }
        printf("%d\n",ans);
    }
    return 0;
}



1012 card card card

第五道过的题,至今还很迷,我们开始看这题的时候也就50几个队过了,但我们AC的时候已经有200+的队AC,可我感觉时间并没有很久啊。

题意是他们告诉我的,按着他们的题意把每个a-b压缩成一个数,然后将数组扩展一倍,求最长的大于等于0的序列和,贪心尺取即可。代码也是他们先写出来的,我好菜啊。只能说助攻了。AC时间:2:07

#include<bits/stdc++.h>
using namespace std;
int AA[2000100],BB[2000010];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++) scanf("%d",&AA[i]), AA[i+n]=AA[i];
        for(int i=0;i<n;i++)
        {
            scanf("%d",&BB[i]);
            BB[i+n]=BB[i];
        }
        int ans=0;
        int SS=0;
        int y=0,x=0,l=0,i=0;
        while(l<n)
        {
            y=0;x=0;
            i=l;
           for(;i<l+n;i++)
           {
               x=x+AA[i]-BB[i];
               y+=AA[i];
               if(x<0)
                break;
           }
           if(y>SS) ans=l,SS=y;
           l=i+1;
        }
        printf("%d\n",ans);
    }
}

 A题卡太久,导致06和10没时间写。貌似新疆赛区的网络赛和沈阳站的都不难,都有原题。。。。。赛后09被吐槽。好吧,在这里也道个歉,最后也交了32发随机。4:40左右有人提议也交随机,此时判题机已经很卡了,我们知道概率很小,看到大家都在疯狂的刷,于是最后10分钟也交了碰碰运气。好吧,我们都不是’天选之人‘。再次对造成影响的队伍表示抱歉。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值