2015 ACM/ICPC Asia Regional Changchun Online

来源:互联网 发布:sgd算法 矩阵分解 编辑:程序博客网 时间:2024/04/30 11:01

更新中.......

<题目链接>

A.Alisha's Party (模拟+优先队列)

题意:Alisha 他邀请了她的朋友参加她的生日party,每个朋友将会给她带一份礼物。由于她的院子不够大,每次他的某个朋友来了之后她就会开门同时放p个人进来(假如外面没有p个人,就把外面的所有人放进来),进来的顺序按礼物的价值由搞到低进,价值相同的则按来的先后顺序进。当最后一个朋友来了之后,就会将所有没有进来的人全部放进去。现在问你q次,每次问你第x个进来的人是谁。

分析:用优先队列直接模拟就好了。注意最后要把所有的人放进来。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 2e5+6;struct node{    int v,id;    bool operator < (const node & t)const    {        if(v!=t.v)            return v<t.v;        return id>t.id;    }}s[maxn];char name[maxn][202];pair <int ,int > p[maxn];priority_queue <node > que;int ans[maxn];int main(){    int ncase,n,k,m,i,j,x,y,q;    scanf("%d",&ncase);    while(ncase--)    {        scanf("%d%d%d",&k,&m,&q);        for(i=1;i<=k;i++)        {            scanf("%s%d",name[i],&s[i].v);            s[i].id=i;        }        for(i=1;i<=m;i++)        {            scanf("%d%d",&x,&y);            p[i]=make_pair(x,y);        }        sort(p+1,p+m+1);                int cur=1,cnt=1,num=0;        for(i=1;i<=m;i++)        {            while(cur<=k && cur<=p[i].first)                que.push(s[cur++]);            num=p[i].second;            while(num-- && !que.empty())            {                ans[cnt++]=que.top().id;                que.pop();            }        }        while(cur<=k)            que.push(s[cur++]);        while(!que.empty())        {            ans[cnt++]=que.top().id;            que.pop();        }        scanf("%d",&x);        printf("%s",name[ans[x]]);        for(i=2;i<=q;i++)        {            scanf("%d",&x);            printf(" %s",name[ans[x]]);        }        printf("\n");    }    return 0;}

B.Ponds(并查集)

题意:给定一个图,将图中所有度小于2的顶点去掉,知道图里面没有度小于2的顶点为止。求剩余的顶点的权值之和(注意题目要求剩余顶点所在的子图里面顶点数目为odd)。

分析:类似拓扑排序,每次去掉度为1的顶点,同时更新并查集。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 2e5+6;struct node{    int v,id;    bool operator < (const node & t)const    {        if(v!=t.v)            return v<t.v;        return id>t.id;    }}s[maxn];char name[maxn][202];pair <int ,int > p[maxn];priority_queue <node > que;int ans[maxn];int main(){    int ncase,n,k,m,i,j,x,y,q;    scanf("%d",&ncase);    while(ncase--)    {        scanf("%d%d%d",&k,&m,&q);        for(i=1;i<=k;i++)        {            scanf("%s%d",name[i],&s[i].v);            s[i].id=i;        }        for(i=1;i<=m;i++)        {            scanf("%d%d",&x,&y);            p[i]=make_pair(x,y);        }        sort(p+1,p+m+1);                int cur=1,cnt=1,num=0;        for(i=1;i<=m;i++)        {            while(cur<=k && cur<=p[i].first)                que.push(s[cur++]);            num=p[i].second;            while(num-- && !que.empty())            {                ans[cnt++]=que.top().id;                que.pop();            }        }        while(cur<=k)            que.push(s[cur++]);        while(!que.empty())        {            ans[cnt++]=que.top().id;            que.pop();        }        scanf("%d",&x);        printf("%s",name[ans[x]]);        for(i=2;i<=q;i++)        {            scanf("%d",&x);            printf(" %s",name[ans[x]]);        }        printf("\n");    }    return 0;}

C.Aggregated Counting (OEIS+离线处理)

题意:给定序列a[1...oo]=1、2、2、3、3、4、4、4.......规律是加a[cur]个cur(cur表示当前值的大小)。比如再往后接,就是接3个5,再接4个6......,给定整数n,最后一个n的位置p1,最后一个p1的位置p2,求p2。比如n=3,最后一个3的位置是5,最后一个5的位置是11,那么答案就是11。

分析:先写个暴力版本的,把前面的几个数丢到OEIS.......发现有公式!!!公式:sigma{i*a[i],1<=i<=n},将和式压缩求解,比如n=3,ans=1*1+(2+3)*2。但是,复杂度为q*sqrt(n),数据比较多,发现计算小的对大的数有帮助,离线处理就好了。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 1e6+6;const LL mod = 1000000007;int a[maxn],cur;struct node{    int id,x;    bool operator < (const node &t)const    {        return x<t.x;    }}q[10000];LL ans[10000];void Init(){    cur=3;    int p=4;    a[1]=1;    a[2]=2;    a[3]=2;    while(1)    {        for(int j=1;j<=a[cur] && p<maxn-1;j++)            a[p++]=cur;        cur++;        if(p>=maxn-1)            break;        }}int Find(int x){    int pos1=lower_bound(a,a+maxn,x+1)-a-1;    return lower_bound(a,a+maxn,pos1+1)-a-1;}inline LL cal(LL s,LL x){    return (s+s+x-1)*x/2%mod;}int getAns(int x){    LL cur=1,len=0,L=0;    LL ret=0;    while(L<x)    {        if(L+a[cur]<=x)        {            ret=(ret+cur*cal(L+1,a[cur]));            if(ret>mod)                ret%=mod;            L+=a[cur];            cur++;        }        else        {            ret=(ret+cur*cal(L+1,x-L));            if(ret>mod)                ret%=mod;            L=x;        }    }    //printf("cur:%lld \n",cur);    return int(ret);}void getall(int n){    LL cur=1,L=0;    LL ret=0;    for(int i=0;i<n;i++)    {        while(L<q[i].x)        {            if(L+a[cur]<q[i].x)            {                ret=(ret+cur*cal(L+1,a[cur]))%mod;                L+=a[cur];                cur++;            }            else            {                ans[q[i].id]=(ret+cur*cal(L+1,q[i].x-L))%mod;                break;            }        }    }}int main(){//    freopen("test.txt","r",stdin);//    freopen("h.txt","w",stdout);    Init();    int ncase,x;    scanf("%d",&ncase);    for(int i=0;i<ncase;i++)    {        scanf("%d",&q[i].x);        q[i].id=i;    }    sort(q,q+ncase);    getall(ncase);    for(int i=0;i<ncase;i++)        printf("%lld\n",ans[i]);    return 0;}


D.Clock Adjusting(待更新)


E.Travel(并查集+离线处理)

题意:给定一个图,有q次查询,每次查询给定一个整数x,问图里面有多少点对(u,v),(u,v)要满足:u<-->v这条路径上的相邻点的路径的权值不大于x。

分析;将边按权值由小到大排序。每查询一次,往图里面添加权值不大于x的边。每个子图就是一个集合,用并查集维护,动态更新答案。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 2e5+6;int fa[maxn],sz[maxn];LL ans[maxn];struct node{    int a,b,v;    bool operator < (const node &t)const    {        return v<t.v;    }}s[maxn];struct que{    int id,x;    bool operator < (const que &t)const    {        return x<t.x;    }}query[maxn];int Find(int rt){    if(fa[rt]==rt)        return rt;    return fa[rt]=Find(fa[rt]);}inline LL cal(LL n){    if(n<2)        return 0;    return n*(n-1)>>1;}void Init(int lim){    for(int i=1;i<=lim;i++)    {        fa[i]=i;        sz[i]=1;    }}LL cur;void update(int a,int b){    int root1=Find(a),root2=Find(b);    if(root1==root2)        return ;    cur-=cal(sz[root1])*2;    cur-=cal(sz[root2])*2;    fa[root1]=root2;    sz[root2]+=sz[root1];    sz[root1]=0;     cur+=cal(sz[root2])*2;}int main(){    int ncase,n,m,i,j,x,y,v,q;    scanf("%d",&ncase);    while(ncase--)    {        scanf("%d%d%d",&n,&m,&q);        Init(n);        for(i=1;i<=m;i++)            scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].v);        sort(s+1,s+m+1);        for(i=1;i<=q;i++)        {            scanf("%d",&query[i].x);            query[i].id=i;        }        sort(query+1,query+q+1);        j=1;        cur=0;        for(i=1;i<=q;i++)        {            for(;j<=m && s[j].v<=query[i].x;j++)                update(s[j].a,s[j].b);            ans[query[i].id]=cur;        }        for(i=1;i<=q;i++)            printf("%lld\n",ans[i]);    }    return 0;}


F.Favorite Donut(后缀数组)

题意:给定长度为n的字符串s(s是环),然后以某一点顺时针或者逆时针出发遍历字符串s将得到一个t,求一个字典序最大的字符串t的起始位置。字典序相同的选起始位置靠前的,位置相同的选顺时针的。

分析:将原来的字符串添加字符,使得前n个字符,每个字符与其后面的n-1个字符正是循环遍历的字符串。比如aabca--->aabcaaabc (顺时针) aabca---->acbaaacba (逆时针),然后对新构造出来的字符串求其后缀数组,那么可以得到字典序最大的两个字符串(两个方向),把字符串取出来,然后比较一下就行了。

ps:对于顺时针的字符串末尾要加一个表示负无穷的字符。这样保证后缀suffix(i)与suffix(j)的LCP等于其中一个后缀的时候,位置靠前的优先选择。

对于逆时针的字符串末尾要加一个表示正无穷的字符。这样保证后缀suffix(i)与suffix(j)的LCP等于其中一个后缀的时候,位置靠后的优先选择。这里的靠后是反转之后的字符串,再反过来就是靠前的了。

代码:

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <set>using namespace std;const int maxn = 1e5+6;char in[maxn],s[maxn];int sa[maxn],t[maxn],t2[maxn],c[maxn],n;void build_sa(int n,int m){int i,*x=t,*y=t2;for(i=0;i<m;i++)c[i]=0;for(i=0;i<n;i++)c[x[i]=s[i]]++;for(i=1;i<m;i++)c[i]+=c[i-1];for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;for(int k=1;k<=n;k<<=1){int p=0;for(i=n-k;i<n;i++)y[p++]=i;for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;for(i=0;i<m;i++)c[i]=0;for(i=0;i<n;i++)c[x[y[i]]]++;for(i=1;i<m;i++)c[i]+=c[i-1];for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];swap(x,y);p=1;x[sa[0]]=0;for(i=1;i<n;i++)x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;if(p>=n)break;m=p;}}void GetSuffix(char buf[],char str[],int p,int n){for(int cnt=0,i=p;cnt<n;cnt++,i++)buf[cnt]=str[i];buf[n]='\0';}char ts[maxn],s1[maxn],s2[maxn];int main(){int ncase,n,i,j,p1,p2,len1,len2;scanf("%d",&ncase);while(ncase--){scanf("%d%s",&n,in);strcpy(ts,in);for(i=0;i<n-1;i++)ts[n+i]=ts[i];ts[n+i]='\0';len1=strlen(ts);strcpy(s,ts);build_sa(len1+1,255);for(i=len1;i>=0 && sa[i]>=n;i--);p1=sa[i];GetSuffix(s1,ts,p1,n);strcpy(ts,in);reverse(ts,ts+n);for(i=0;i<n-1;i++)ts[n+i]=ts[i];ts[n+i]='\0';len2=strlen(ts);ts[len2]='z'+1;strcpy(s,ts);build_sa(len2+1,255);for(i=len2;i>=0 && sa[i]>=n;i--) ;p2=sa[i];GetSuffix(s2,ts,p2,n);//printf("%s\n%s\n",s1,s2);p1=p1+1;p2=n-p2;int temp=strcmp(s1,s2);if(temp!=0)temp==1?printf("%d 0\n",p1):printf("%d 1\n",p2);else if(p1!=p2)p1<p2?printf("%d 0\n",p1):printf("%d 1\n",p2);elseprintf("%d 0\n",p1);}return 0;}

G.The Water Problem (暴力)

题意:查询区间最大值,数据范围很小。

分析直接暴力查询就好,比赛的时候没看数据范围,用的线段树。。。。。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;int a[1005];int main(){    int ncase;    scanf("%d",&ncase);    while(ncase--)    {        int n,q,i,j,m,l,r;        scanf("%d",&n);        for(i=1;i<=n;i++)            scanf("%d",&a[i]);        scanf("%d",&q);        while(q--)        {            m=-(1<<30);            scanf("%d%d",&l,&r);            for(i=l;i<=r;i++) if(a[i]>m)                m=a[i];            printf("%d\n",m);        }    }    return 0;}


H. Elven Postman(二叉树)

题意:给定一棵排序二叉树的先序遍历,给定整数x,求从根节点到x的路径。

分析:暴力模拟。先把树建起来,然后由于是排序二叉树,直接走就行了。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 1005;struct node{    int v;    node *lson,*rson;    node()    {        lson=rson=nullptr;    }    node(int x,node *l=nullptr,node *r=nullptr)    :v(x),lson(l),rson(r) {}};int a[maxn];node* build(int x,int y){    if(x>y)        return nullptr;    if(x==y)        return new node(a[x]);    int cur=x+1;    while(cur<=y && a[cur]<a[x])        cur++;    node *L=build(x+1,cur-1);    node *R=build(cur,y);    return new node(a[x],L,R);}void check(node *root){    if(root!=nullptr)    {        check(root->lson);        printf("%d ",root->v);        check(root->rson);    } }void print(node *root,int x){    if(x==root->v)        return ;    if(x<root->v)    {        putchar('E');        print(root->lson,x);    }    else    {        putchar('W');        print(root->rson,x);    }}int main(){    int ncase,n,q,i,j,x;    scanf("%d",&ncase);    while(ncase--)    {        scanf("%d",&n);        for(i=1;i<=n;i++)            scanf("%d",&a[i]);        node *root=build(1,n);        scanf("%d",&q);        while(q--)        {            scanf("%d",&x);            if(root->v==x)            {                puts("");                continue ;            }            print(root,x);            puts("");        }    }     return 0;}


I.Food Problem(待更新)


J.Unknow Treasure(lucas+crt)

题意:求C(n,m)%M,M<10^18。M=p1*p2.....pk,(1<=k<=10)and (pi<100 000,pi is  s prime)。

分析:直接用lucas求出C(n,m)%pi的值,然后用中国剩余定理把这些值合并。可惜啊,模版题不会,要不然当时就出线了。

代码

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const int N =150000;LL fac[N];void init(LL p){    int i;    fac[0] =1;    for(i =1; i <= p; i++)        fac[i] = fac[i-1]*i % p;}LL mul(LL a,LL b,LL p){    if(a==0 || b==0)        return 0;    int fg=1;    if(a<0 && b<0)    {        a=-a;        b=-b;    }    else if(a<0 || b<0)    {        if(a<0)            a=-a;        else            b=-b;        fg=-1;    }    LL ret(0);    while(b)    {        if(b&1)            ret=(ret+a)%p;        a=(a<<1)%p;        b>>=1;    }    return ret*fg;}LL myPow(LL a,LL n,LL p){    LL ret=1;    while(n)    {        if(n&1)            ret=mul(ret,a,p);        n>>=1;        a=mul(a,a,p);    }    return ret;}LL C(LL n,LL m,LL p){    if(m > n)  return 0;    return  fac[n]*myPow(fac[m]*fac[n-m], p-2,p)%p;}LL Lucas(LL n, LL m,LL p){    if(m ==0)  return 1;    else return  (C(n%p, m%p,p)*Lucas(n/p, m/p,p))%p;}LL r[100],mo[100];void gcd(LL a,LL b,LL &d,LL &x,LL &y){    if(!b)    {        d=a;        x=1;        y=0;        return ;    }    gcd(b,a%b,d,y,x);    y-=x*(a/b);}LL china(int n)   //x=r[i] (mod m[i]){    LL M=1;    LL i,Mi,x,y,d,ans=0;    for(i=1;i<=n;i++)        M*=mo[i];    for(i=1;i<=n;i++)    {        Mi=M/mo[i];        gcd(Mi,mo[i],d,x,y);        //printf("%lld %lld %lld \n",Mi,x,r[i]);        LL temp=mul(Mi,x,M);        temp=mul(temp,r[i],M);        ans=(ans+temp+M)%M;      //  ans=(ans+Mi*x*r[i])%M;    }    return (ans+M)%M;}int main(){    int t,k,i,j;    scanf("%d",&t);    while(t--)    {        LL n,m,p;        scanf("%lld%lld%d",&n,&m,&k);        for(i=1;i<=k;i++)        {            scanf("%lld",&p);            init(p);            r[i]=Lucas(n,m,p);            mo[i]=p;            }                                                                                                                                                                printf("%lld\n",china(k));    }    return 0;}

K.Good Numbers(待更新)


L.Marisa's Cake(待更新)


M.Robot Dog(待更新)


0 0