zoukankan      html  css  js  c++  java
  • 2016中国大学生程序设计竞赛(长春)-重现赛 题解

    E.The Fastest runner ms.zhang

    给一个图,n点n边,问走过所有点的最优方案(总路程最小;其次,起点的序号最小;再次,终点的序号最小)

    网上有代码的其实。。。只是看不惯那冗长的代码和较慢的运行速度,就决定自己写一个

    整棵树只有n-1边,这种图的话就是一个环连着几棵子树

    s和t之间肯定有1条路只走1次,其他路走2次

    最短路程有2种情况:

    s和t最短路不过环,则st最短路全程在树上,环只走1次,设dis(s,t)是s和t间最短路,looplength是环长度,则答案是2*n-looplength-dis(s,t)

    这部分树dp就能解决,不过要注意最大次大值的取值问题(最后的WA就是因为这个)和序号的最小化

    s和t最短路过环,则st通路有环部分,由于这种情况环部分的路径不可能覆盖全环,没有被通路覆盖的部分路必须走2次

    由于只要覆盖全点,走2次那部分有1条边不用走,少走2次

    通路可以选择上面或下面,这样就要选择最长覆盖以减少走2次的路

    求这个要用扫描线o(n)的方法防超时

    求出区间前缀(不横跨环上0点)和后缀(横跨环上0点)里面元素的最大值与扫描线的距离

    距离是dep[i]+i+dep[j]-j,就是说随着扫描线和区间内最大值的变化,要加减的值是按它们序号来的

    同样要注意序号最小化

    被坑了2天,真不该(坑点:序号最小化)

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<queue>
    #include<stack>
    #include<math.h>
    #include<vector>
    #include<map>
    #include<set>
    #include<stdlib.h>
    #include<cmath>
    #include<string>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    typedef __int64 ll;
    const int N=200025;
    int max(int a,int b){return a>b?a:b;}
    int min(int a,int b){return a<b?a:b;}
    int cnt;
    int head[N],inu[N],circ[N];
    int dep[N][2],pto[N][2],cto[N][2];
    int que[N],lop[N];
    int to[N<<1],nxt[N<<1];
    void addedge(int u,int v)
    {
        to[cnt]=v;nxt[cnt]=head[u];head[u]=cnt++;
        to[cnt]=u;nxt[cnt]=head[v];head[v]=cnt++;
    }
    //相同路径数的索引号选择
    void isvalid(int& a,int& b,int c,int d)
    {
        int cmpr=min(a,b)-min(c,d);
        if(cmpr>0 || (cmpr==0 && max(a,b)>max(c,d)))
            a=c,b=d;
    }
    void bfs(int n)
    {
        memset(dep,0,sizeof(dep));
        memset(circ,0,sizeof(circ));
        int tai=0,hed=0,i,u,v;
        for(i=0;i<n;i++)
        {
            if(inu[i]==1)que[tai++]=i;
            pto[i][0]=pto[i][1]=cto[i][0]=cto[i][1]=i;//深度为0,则端点就是本身
        }
        while(tai!=hed)
        {
            u=que[hed++];inu[u]--;
            if(circ[u]==dep[u][0]+dep[u][1])
            {
                isvalid(cto[u][0],cto[u][1],pto[u][0],pto[u][1]);
            }
            else if(circ[u]<dep[u][0]+dep[u][1])
            {
                circ[u]=dep[u][0]+dep[u][1];
                cto[u][0]=pto[u][0];
                cto[u][1]=pto[u][1];
            }
            for(i=head[u];i!=-1;i=nxt[i])
            {
                v=to[i];
                if(inu[v]<=0)continue;
                if(dep[v][0]<dep[u][0]+1)
                {
                    if(dep[v][1]<dep[v][0])
                    {
                        dep[v][1]=dep[v][0];
                        pto[v][1]=pto[v][0];
                    }
                    else if(dep[v][1]==dep[v][0] && pto[v][1]>pto[v][0])
                    {
                        pto[v][1]=pto[v][0];
                    }
                    dep[v][0]=dep[u][0]+1;
                    pto[v][0]=pto[u][0];
                }
                else if(dep[v][0]==dep[u][0]+1 && pto[v][0]>pto[u][0])
                {
                    pto[v][0]=pto[u][0];
                }
                else if(dep[v][1]<dep[u][0]+1)
                {
                    dep[v][1]=dep[u][0]+1;
                    pto[v][1]=pto[u][0];
                }
                else if(dep[v][1]==dep[u][0]+1 && pto[v][1]>pto[u][0])
                {
                    pto[v][1]=pto[u][0];
                }
                if(circ[v]==circ[u])
                {
                    isvalid(cto[v][0],cto[v][1],cto[u][0],cto[u][1]);
                }
                else if(circ[v]<circ[u])
                {
                    circ[v]=circ[u];
                    cto[v][0]=cto[u][0];
                    cto[v][1]=cto[u][1];
                }
                if(--inu[v]==1)
                {
                    que[tai++]=v;
                }
            }
        }
    }
    int main()
    {
        int t,n,u,v,ans,bestu,bestt,i;
        scanf("%d",&t);
        for(int h=1;h<=t;h++)
        {
            ans=99999999;bestu=99999999;bestt=99999999;
            memset(head,-1,sizeof(head));cnt=0;
            memset(inu,0,sizeof(inu));
            scanf("%d",&n);
            for(i=0;i<n;i++)
            {
                scanf("%d%d",&u,&v);u--,v--;
                addedge(u,v),inu[u]++,inu[v]++;
            }
            bfs(n);
            int lpl=0,max1=-99999999,pmax1=99999999,max2=-99999999,pmax2=99999999;
            for(i=0;inu[i]==0;i++);
            for(u=i;inu[u]>=2;u=to[v])
            {
                lop[lpl++]=u;
                for(v=head[u];v!=-1 && inu[to[v]]<2;v=nxt[v]);
                if(v==-1)break;
                inu[u]--;
            }
            lop[lpl]=lop[0];
            for(i=0;i<lpl;i++)
            {
                if(circ[lop[i]]==dep[lop[i]][0]+dep[lop[i]][1])
                {
                    isvalid(cto[lop[i]][0],cto[lop[i]][1],pto[lop[i]][0],pto[lop[i]][1]);
                }
                else if(circ[lop[i]]<dep[lop[i]][0]+dep[lop[i]][1])
                {
                    circ[lop[i]]=dep[lop[i]][0]+dep[lop[i]][1];
                    cto[lop[i]][0]=pto[lop[i]][0];
                    cto[lop[i]][1]=pto[lop[i]][1];
                }
            }
            for(i=0;i<lpl;i++)
            {
                int tmp=2*n-lpl-circ[lop[i]];//s and t are put in the same trees
                if(ans==tmp)
                {
                    isvalid(bestu,bestt,cto[lop[i]][0],cto[lop[i]][1]);
                }
                else if(ans>tmp)
                {
                    ans=tmp;
                    bestu=cto[lop[i]][0];
                    bestt=cto[lop[i]][1];
                }
                //notice:The loop can walk without 1 edge
                if(pmax1!=99999999)
                {
                    tmp=2*n-(i+dep[lop[i]][0]+max1)-2;
                    if(ans==tmp)
                    {
                        isvalid(bestu,bestt,pto[lop[i]][0],pto[lop[pmax1]][0]);
                    }
                    else if(ans>tmp)
                    {
                        ans=tmp;
                        bestu=pto[lop[i]][0];
                        bestt=pto[lop[pmax1]][0];
                    }
                }
                if(max1<dep[lop[i]][0]-i)
                {
                    max1=dep[lop[i]][0]-i;
                    pmax1=i;
                }
                else if(max1==dep[lop[i]][0]-i && pto[lop[pmax1]][0]>pto[lop[i]][0])
                {
                    pmax1=i;
                }
                if(pmax2!=99999999)
                {
                    tmp=2*n-(lpl-i+dep[lop[lpl-i-1]][0]+max2)-2;
                    if(ans==tmp)
                    {
                        isvalid(bestu,bestt,pto[lop[lpl-i-1]][0],pto[lop[pmax2]][0]);
                    }
                    else if(ans>tmp)
                    {
                        ans=tmp;
                        bestu=pto[lop[lpl-i-1]][0];
                        bestt=pto[lop[pmax2]][0];
                    }
                }
                if(max2<dep[lop[lpl-i-1]][0]+i)
                {
                    max2=dep[lop[lpl-i-1]][0]+i;
                    pmax2=lpl-i-1;
                }
                else if(max2==dep[lop[lpl-i-1]][0]+i && pto[lop[pmax2]][0]>pto[lop[lpl-i-1]][0])
                {
                    pmax2=lpl-i-1;
                }
            }
            printf("Case #%d: %d %d %d
    ",h,ans,min(bestu,bestt)+1,max(bestu,bestt)+1);
        }
        return 0;
    }
    View Code

    G.Instability

    给一个图,问最少3个元素,含独立集或团的集合有多少个

    其实很简单的,6个点的话,不是含团就是含独立集

    枚举3~5个点的集合,时间复杂度最多c(50,3)+c(50,4)+c(50,5)<c(50,5)*3=2118760*3

    然后就AC了

    I. Sequence II

    题意:不修改数列的数,在线查询区间中位数

    题解:这种没有修改的当然是主席树大法好,为了这个还专门学了一点时间

    这个链接http://www.cnblogs.com/zyf0163/p/4749042.html最好懂

    就是把第k数据转换为arc(a(k)),然后作为元素插入树k~树n的第arc(a(k))个结点

    由于存在大量同构树,可以直接连接而不是新建树结点,这样就节省了memory&&time

    至于中位数的查询,先查询[l,r]下有多少不同的数->u,再从l开始查第ceil(u/2)大数

    /* ***********************************************
    ┆  ┏┓   ┏┓ ┆
    ┆┏┛┻━━━┛┻┓ ┆
    ┆┃       ┃ ┆
    ┆┃   ━   ┃ ┆
    ┆┃ ┳┛ ┗┳ ┃ ┆
    ┆┃       ┃ ┆
    ┆┃   ┻   ┃ ┆
    ┆┗━┓ 马 ┏━┛ ┆
    ┆  ┃ 勒 ┃  ┆      
    ┆  ┃ 戈 ┗━━━┓ ┆
    ┆  ┃ 壁     ┣┓┆
    ┆  ┃ 的草泥马  ┏┛┆
    ┆  ┗┓┓┏━┳┓┏┛ ┆
    ┆   ┃┫┫ ┃┫┫ ┆
    ┆   ┗┻┛ ┗┻┛ ┆
    ************************************************ */
    //#pragma comment(linker, "/STACK:102400000,102400000")
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <set>
    #include <map>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <bitset>
    using namespace std;
    
    #define rep(i,a,b) for (int i=(a),_ed=(b);i<=_ed;i++)
    #define per(i,a,b) for (int i=(b),_ed=(a);i>=_ed;i--)
    #define pb push_back
    #define mp make_pair
    const int inf_int = 2e9;
    const long long inf_ll = 2e18;
    #define inf_add 0x3f3f3f3f
    #define mod 1000000007
    #define LL long long
    #define ULL unsigned long long
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define SelfType int
    SelfType Gcd(SelfType p,SelfType q){return q==0?p:Gcd(q,p%q);}
    SelfType Pow(SelfType p,SelfType q){SelfType ans=1;while(q){if(q&1)ans=ans*p;p=p*p;q>>=1;}return ans;}
    #define Sd(X) int (X); scanf("%d", &X)
    #define Sdd(X, Y) int X, Y; scanf("%d%d", &X, &Y)
    #define Sddd(X, Y, Z) int X, Y, Z; scanf("%d%d%d", &X, &Y, &Z)
    #define reunique(v) v.resize(std::unique(v.begin(), v.end()) - v.begin())
    #define all(a) a.begin(), a.end()
    #define   mem(x,v)      memset(x,v,sizeof(x))
    typedef pair<int, int> pii;
    typedef pair<long long, long long> pll;
    typedef vector<int> vi;
    typedef vector<long long> vll;
    inline int read(){int ra,fh;char rx;rx=getchar(),ra=0,fh=1;while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();if(rx=='-')fh=-1,rx=getchar();while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;}
    
    const int N = 2e5 + 5;
    
    struct node
    {
        int l,r,sum;
    }tree[N*40];
    
    int tot,cnt;
    
    void update(int l,int r,int &x,int y,int k,int val)
    {
        int tmp = x;
        x = ++tot;
        tree[x] = tmp ? tree[tmp] : tree[y];
        tree[x].sum += val;
        if(l==r) return;
        int mid = (l+r) >> 1;
        if(k<=mid) update(l,mid,tree[x].l,tree[y].l,k,val);
        else update(mid+1,r,tree[x].r,tree[y].r,k,val);
    }
    
    int query(int l,int r,int x,int k)
    {
        if(l==r) return l;
        int mid = (l+r) >> 1;
        int sum = tree[tree[x].l].sum;
        if(k<=sum) return query(l,mid,tree[x].l,k);
        else return query(mid+1,r,tree[x].r,k-sum);
    }
    
    int getsum(int l,int r,int x,int L,int R)
    {
        if(L<=l && r<=R) return tree[x].sum;
        int mid = (l+r) >> 1;
        int res = 0;
        if(L<=mid) res += getsum(l,mid,tree[x].l,L,R);
        if(R>mid) res += getsum(mid+1,r,tree[x].r,L,R);
        return res;
    }
    
    int a[N],pos[N],rt[N];
    int ans[N];
    
    void init()
    {
        tot = 0;
        MS0(pos);
        MS0(rt);
    }
    
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        //ios::sync_with_stdio(0);
        //cin.tie(0);
        int cas = 1;
        int t = read();
        while(t--)
        {
            int n = read(), m = read();
            init();
            for(int i=1;i<=n;i++) a[i] = read();
            for(int i=n;i;i--)//倒着插入,以便从l开始计数
            {
                if(pos[a[i]])
                {
                    update(1,n,rt[i],rt[i+1],pos[a[i]],-1);//去除重复的数
                }
                update(1,n,rt[i],rt[i+1],i,1);//前缀树继承上一次连接的
                pos[a[i]] = i;
            }
            for(int i=1;i<=m;i++)
            {
                int l = read(), r = read();
                l = (l + ans[i-1]) % n + 1;
                r = (r + ans[i-1]) % n + 1;
                if(l>r) swap(l,r);
                int sum = (getsum(1,n,rt[l],l,r) + 1) / 2;//向上取整,题目要求
                ans[i] = query(1,n,rt[l],sum);
            }
            printf("Case #%d:",cas++);
            for(int i=1;i<=m;i++) printf(" %d",ans[i]);
            printf("
    ");
        }
    
    
        return 0;
    }
    View Code

    J. Ugly problem

    题意:把大数字拆成不超过50个的回文数

    题解:每次用不超过大数字的回文数去减,得到的结果要再这样执行,注意"10"这个数的坑点

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <string>
    #include <cstdlib>
    #include <vector>
    #include <set>
    #include <map>
    using namespace std;
    const double eps=1e-8;
    int len;
    //大数乘法
    void subtract(char *a,char *b,char *c)
    {
        for(int i=len-1;i>=0;i--)
        {
            c[i]=a[i]-b[i]+'0';
            if(c[i]<'0')c[i]+=10,a[i-1]--;
        }
    }
    
    char str[2][1005],sub[50][1005];
    int main()
    {
        int i,j,k,n;
        scanf("%d",&n);getchar();
        for(int h=1;h<=n;h++)
        {
            memset(sub,'0',sizeof(sub));
            gets(str[0]);
            len=strlen(str[0]);
            int old=1,now=0;
            int cnt=0;
            for(i=0;str[now][i];)//前置0到末尾,整个数就是0
            {
                old^=1,now^=1;//滚动数组
                if(!strcmp(str[old]+i,"10"))//10=9+1
                {
                    sub[cnt][0]='9';sub[cnt][1]=0;cnt++;
                    sub[cnt][0]='1';sub[cnt][1]=0;cnt++;
                    break;
                }
                for(j=i;j<=len-1-j+i && str[old][j]<=str[old][len-1-j+i];j++);
                strcpy(sub[cnt],str[old]);
                if(j<len-1-j+i)//对折后有小于的,就只能取较小的回文数
                {
                    sub[cnt][(i+len-1)/2]--;
                    for(j=(i+len-1)/2;j>=0 && sub[cnt][j]<'0';j--)sub[cnt][j]+=10,sub[cnt][j-1]--;
                }
                for(j=0;sub[cnt][j]=='0';j++);//去前导0
                for(k=j;k<=len-1-k+j;k++)sub[cnt][len-1-k+j]=sub[cnt][k];//根据前半串对折构造后半串
                sub[cnt][len]=0;
                subtract(str[old],sub[cnt],str[now]);
                for(;str[now][i]=='0';i++);
                cnt++;
            }
            printf("Case #%d:
    %d
    ",h,cnt);
            for(i=0;i<cnt;i++)
            {
                char *s;
                for(s=sub[i];*s=='0';s++);
                puts(s);
            }
        }
        return 0;
    }
    ugly problem

     K.Binary Indexed Tree

    题意:分别对树状数组执行add(r,1)和add(l-1,-1)之后,发生改变的结点数量是s(l,r)

    求[1,n]里所有区间的s(l,r)和

    题解:发现相同右区间的那些s(l,r)的总和有规律

    如12,在右区间是12~15时贡献是12,到了>=16的右区间,贡献变成4

    可以发现贡献分量与二进制有关这不废话吗!为什么非要暴力计算不可呢

    可以理解为异或,把不同二进制数1的个数统计起来,然后答案其实很明显:

    sum_{r=1}^{n}sum_{l=0}^{r-1}(cnt_l+cnt_r-2cnt_{lcp(l,r)})=frac{1}{2}sum_{l=0}^{n}sum_{r=0}^{n}(cnt_l+cnt_r-2cnt_{lcp(l,r)})r=1n​​l=0r1​​(cntl​​+cntr​​2cntlcp(l,r)​​)=21​​l=0n​​r=0n​​(cntl​​+cnt​r​​−2cnt​lcp(l,r)​​)

    其中cnt(l)是l作为二进制时1的个数  lcp(l,r)指二进制时l和r的最长公共前缀

    减2次lcp(l,r)是异或,减1次的那种是位或运算

    为什么直接就是1/2?l和r相等的情况直接被减成0了,就不用考虑容斥

    转换成这个,什么都好算

    前面2个就是照规律计数,后面打表出来发现规律也可以算。。。不就是把前面的一维计数对应部位平方吗

    这个方法最简单了,不像其他方法需要复杂的计算步骤

    //Thanks to Fsss_7
    #include<map>
    #include<set>
    #include<cmath>
    #include<queue>
    #include<bitset>
    #include<math.h>
    #include<vector>
    #include<string>
    #include<stdio.h>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #pragma comment(linker, "/STACK:102400000,102400000")
    using namespace std;
    const int N=400010;
    const int M=50010;
    const int mod=1000000007;
    const int MOD1=1000000007;
    const int MOD2=1000000009;
    const double EPS=0.00000001;
    typedef long long ll;
    const ll MOD=1000000007;
    const int INF=1000000010;
    const ll MAX=1ll<<55;
    const double eps=1e-5;
    const double inf=~0u>>1;
    const double pi=acos(-1.0);
    typedef long double db;
    typedef unsigned int uint;
    typedef unsigned long long ull;
    int a[100];
    ll b[100],c[100],e[100];
    ll get(int len) {
        ll ret=0;
        for (int i=0;i<len;i++) ret+=a[i];
        for (int i=len-1;i>=0;i--)
        if (a[i]) {
            if (i) (ret+=c[i-1])%MOD;
            (ret+=b[i+1]*e[i]%MOD)%=MOD;
        } else (ret+=b[i+1]*e[i]%MOD)%=MOD;
        return ret;
    }
    ll getlcp(int len) {
        ll ret=0;
        for (int i=len-1;i>=0;i--)
        if (a[i]) {
            if (i) (ret+=(c[i-1]+1)*(c[i-1]+1)%MOD)%=MOD;
            else (ret+=1ll)%=MOD;
            (ret+=b[i+1]*e[i]%MOD*e[i]%MOD)%=MOD;
        } else (ret+=b[i+1]*e[i]%MOD*e[i]%MOD)%=MOD;
        return ret;
    }
    int main()
    {
        int i,ca,T,len;
        ll n,m,ans;
        scanf("%d", &T);
        for (i=0;i<63;i++) e[i]=(1ll<<i)%MOD;
        for (ca=1;ca<=T;ca++) {
            scanf("%I64d", &n);m=n;
            for (len=0;m;m>>=1) a[len++]=m%2;
            b[len]=0;c[0]=a[0];
            for (i=1;i<len;i++) c[i]=(c[i-1]+(a[i]*1ll<<i))%MOD;
            for (i=len-1;i>=0;i--) b[i]=((b[i+1]<<1)+a[i])%MOD;
            ans=((n+1)%MOD*get(len))%MOD;
            printf("Case #%d: %I64d
    ", ca, ((ans-getlcp(len))%MOD+MOD)%MOD);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    html——meta标签、link标签
    html——a标签中target属性
    html——相对路径、绝对路径(有待补充....)
    CSS——display:flex
    JS——AJAX
    JS——锚点的运用
    Json——转义符
    C#——工厂模式
    C#——反射动态创建类的实例
    “微信跳一跳”辅助脚本的一种简单实现
  • 原文地址:https://www.cnblogs.com/dgutfly/p/5930790.html
Copyright © 2011-2022 走看看