• 2017北京国庆刷题Day2 morning


    期望得分:100+100+40=240

    实际得分:100+40+0=140

    T1 一道图论神题(god)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

    LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,只有点权。

    LYK想把这个图删干净,它的方法是这样的。每次选择一个点,将它删掉,但删这个点是需要代价的。假设与这个点相连的还没被删掉的点是u1,u2,…,ukLYK将会增加a[u1],a[u2],…,a[uk]的疲劳值。

    它想将所有点都删掉,并且删完后自己的疲劳值之和最小。你能帮帮它吗?

    输入格式(god.in)

    第一行两个数n,m表示一张n个点m条边的图。

    第二行n个数ai表示点权。

    接下来m行每行三个数u,v,表示有一条连接u,v的边。数据保证任意两个点之间最多一条边相连,并且不存在自环。

    输出格式(god.out)

    你需要输出这个最小疲劳值是多少。

    输入样例

    4 3

    10 20 30 40

    1 4

    1 2

    2 3

    输出样例

    40

    样例解释

    一个合理的方法是先删4号点,此时有10点疲劳值。接下来删3号点,获得20点疲劳值,再删2号点,获得10点疲劳值,最后删1号点,没有疲劳值。总计40点疲劳值。

    对于30%的数据n<=10。

    对于60%的数据n,m<=1000。

    对于100%的数据1<=n,m,ai<=100000

    m条边,疲劳值就会计算m次

    所以可以贪心的选择 每条边的疲劳值小的点累计

    正确性:如果每条无向边改为疲劳值大的点向疲劳值小的点的有向边,那么这张图就是DAG

    #include<cstdio>
    #include<algorithm>
    #define N 100001
    using namespace std;
    int val[N];
    void out(long long x)
    {
        if(x/10) out(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
        freopen("god.in","r",stdin);
        freopen("god.out","w",stdout);
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&val[i]);
        int u,v;
        long long ans=0;
        for(int i=1;i<=m;i++) 
        {
            scanf("%d%d",&u,&v);
            ans+=min(val[u],val[v]);
        }
        out(ans);
    }
    View Code

    T2 位运算2(bit)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

    LYK拥有一个十进制的数N。它赋予了N一个新的意义:不考虑N的符号,将N每一位都拆开来后再加起来就是N所拥有的价值。例如数字123拥有6的价值,数字999拥有27的价值,数字-233拥有8的价值。

    假设数字N的价值是K,LYK想找到一个价值是K+1的数字,当然这个答案实在太多了,LYK想使得这个价值为K+1的数字尽可能大,并且需要保证这个数字小于N。

    输入格式(bit.in)

    一个整数N。

    输出格式(bit.out)

    一个数表示答案。你需要输出一个整数,且这个数不包含前导0。

    输入样例1

    199

    输出样例1

    -299

    输入样例2

    1520

    输出样例2

    1512

    对于20%的数据|N|<=10

    对于40%的数据|N|<=100

    对于60%的数据|N|<=10^9

    对于80%的数据|N|<=10^1000

    对于100%的数据|N|<=10^100000。

    正数和负数分开讨论

    正数:

    ① 设第i位之后的数位之和为gather,第i位为num,那么如果gather<=(len-i)*9+num-1,

    最优解就是第i位为num-1,之后的数贪心填当前能填的最大的,之前就是原数

    ② 找不到满足条件①的,最优解只能是负数,那就是找绝对值最小的那个

    设数位之和为tot,那么第一位为tot%9,后面tot/9个9

    负数:

    从后往前找到第一个小于9的位置,这个位置的数+1,其余位置的数不变

    考场上忘了判断负数,WA了5个点

    正数的情况①中,没有判断第i位可能是要输出的第一位,且num-1=0,WA了1个点

    #include<cstdio>
    #include<cstring>
    using namespace std;
    char s[100005];
    int num[100005],tot,gather;
    int main()
    {
        freopen("bit.in","r",stdin);
        freopen("bit.out","w",stdout);
        scanf("%s",s);
        int len=strlen(s);
        if(s[0]!='-')
        {
            for(int i=0;i<len;i++) num[i]=s[i]-'0',tot+=num[i];
            gather=num[len-1]+1; 
            for(int i=len-2;i>=0;i--)
            {
                gather+=num[i];
                if(!num[i]) continue;
                if((len-i-1)*9+num[i]-1>=gather)
                {
                    bool ok=false;
                    for(int j=0;j<i;j++) ok=true,printf("%d",num[j]);
                    if(ok || num[i]>1) printf("%d",num[i]-1); 
                    gather-=(num[i]-1);
                    for(int j=i+1;j<len;j++)
                        if(gather>=9) printf("9"),gather-=9;
                        else printf("%d",gather),gather=0;
                    return 0;
                }
            }
            putchar('-');
            tot++;
            int n=tot/9;
            if(tot%9) printf("%d",tot%9);
            for(int i=1;i<=n;i++) printf("9");
            return 0;
        }
        else
        {
            putchar('-');
            for(int i=1;i<len;i++) num[i]=s[i]-'0';
            for(int i=len-1;i;i--)
                if(num[i]<9) 
                {
                    for(int j=1;j<i;j++) printf("%d",num[j]);
                    printf("%d",num[i]+1);
                    for(int j=i+1;j<len;j++) printf("%d",num[j]);
                    return 0;
                }
            printf("1");
            for(int i=1;i<len;i++) printf("%d",num[i]);
            return 0;
        }
    }
    View Code

    T3 逆序对(pair)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

    LYK最近在研究逆序对。

    这个问题是这样的。

    一开始LYK有一个2^n长度的数组ai。

    LYK有Q次操作,每次操作都有一个参数k。表示每连续2^k长度作为一个小组。假设n=4,k=2,则a[1],a[2],a[3],a[4]为一个小组,a[5],a[6],a[7],a[8]为一个小组,a[9],a[10],a[11],a[12]为一个小组,a[13],a[14],a[15],a[16]也为一个小组。

    然后LYK对于每个小组都翻转,也就是说原数组会变成a[4],a[3],a[2],a[1],a[8],a[7],a[6],a[5],a[12],a[11],a[10],a[9],a[16],a[15],a[14],a[13]。之后它想求出这2^n个数的逆序对是多少。

    因此你需要输出对于每次操作,操作完后这2^n个数的逆序对有多少对。

    两个数ai,aj被称为逆序对当且仅当i<j且ai>aj。

    输入格式(pair.in)

    第一行一个数n。

    接下来一行2^n个数ai表示一开始的数组。

    接下来一行一个数Q,表示操作的次数。

    接下来一行Q个数,表示每次操作的参数k。

    输出格式(pair.out)

    Q行,表示每次操作后的答案。

    输入样例

    2

    2 1 4 3

    4

    1 2 0 2

    输出样例

    0

    6

    6

    0

    样例解释

    第一次操作,{2,1,4,3}->{1,2,3,4}

    第二次操作,{1,2,3,4}->{4,3,2,1}

    第三次操作,{4,3,2,1}->{4,3,2,1}

    第四次操作,{4,3,2,1}->{1,2,3,4}

    对于30%的数据n<=10Q<=10。

    对于50%的数据n<=10,Q<=1000。

    对于80%的数据n<=10,Q<=200000。

    对于100%的数据n<=17Q<=200000,1<=ai<=2^n。

    例:1 2 3 4 5 6 7 8  k=3

    翻转结果为 8 7 6 5 4 3 2 1

    将翻转过程拆分:

    第一步: 2 1 4 3 6 5 8 7

    第二步:4 3 2 1 8 7 6 5

    第三步:8 7 6 5 4 3 2 1

    所以每一次翻转都可以拆分,而拆分的过程就是交换相邻的2^(步数-1)

    所以归并排序求逆序对的时候,用st表维护从第i个位置,长为2^j的区间的逆序对个数

    然后求出对应区间的顺序对个数

    维护所有长为2^i的区间的逆序对个数rev[i]和顺序对个数pos[i]

    统计答案的时候,拆分就是swap(rev,pos)

    在这里顺序对个数是用总个数-逆序对个数求的

    所以就会有一个问题,例:

    2 2 3 3

    实际上的:

    pos : 0  0

    rev :  0  0

    但求的时候,pos :2  4

    因为用总个数-逆序对个数=顺序对个数+相等的数对

    所以归并过程中,还要预处理从第i个位置,长为2^j的区间的相等数对个数

    计算rev,pos时,同时计算same[i],表示所有长为2^i的区间相等的数对个数

    求解的时候 先swap(rev,pos),然后rev-=same,再 pos=总的-rev

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define N 17
    //#define M 1<<N
    
    using namespace std;
    
    typedef long long LL;
    
    const int M=1<<17;
    
    int n,tot;
    int a[M+2],res[M+2],bit[M+2],P[M+2];
    LL st[M+2][N+1],ST[M+2][N+1];
    LL rev[N+1],pos[N+1],same[N+1],sum[N+1];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void gbsort(int l,int r)
    {
        if(l==r) return;
        int mid=l+r>>1;
        gbsort(l,mid); 
        gbsort(mid+1,r);
        for(int p=r;p>=l;--p) P[p]=p;
        for(int p=r-1;p>=l;--p)
            if(a[p]==a[p+1]) P[p]=P[p+1]; 
        int i=l,j=mid+1,k=l;
        while(i<=mid && j<=r)
        {
            if(a[i]>a[j]) st[l][bit[r-l+1]]+=mid-i+1,res[k++]=a[j++];
            else if(a[i]==a[j]) ST[l][bit[r-l+1]]+=P[j]-j+1,res[k++]=a[i++];
            else res[k++]=a[i++];
        }
        while(i<=mid) res[k++]=a[i++];
        while(j<=r) res[k++]=a[j++];
        for(k=l;k<=r;++k) a[k]=res[k];
    }
    
    void pre()
    {
        for(int i=1;i<=n;++i) sum[i]=1ll*(1<<i-1)*(1<<i-1);
        for(int i=1,len=2;i<=n;++i,len<<=1)
            for(int j=1;j<=tot;j+=len)
                rev[i]+=st[j][i],pos[i]+=sum[i]-st[j][i],same[i]+=ST[j][i];    
    }
    
    int main()
    {
        freopen("pair.in","r",stdin);
        freopen("pair.out","w",stdout);
        read(n); tot=1<<n;
        for(int i=2;i<=tot;++i) bit[i]=bit[i>>1]+1;
        for(int i=1;i<=tot;++i) read(a[i]);
        gbsort(1,tot);
        pre();
        int m,k; read(m);
        LL ans;
        while(m--)
        {
            read(k);ans=0;
            for(int i=1;i<=k;++i) swap(rev[i],pos[i]),rev[i]-=same[i],pos[i]=sum[i]*tot/(1<<i)-rev[i];
            for(int i=1;i<=n;++i) ans+=rev[i];
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    View Code

    考场上40分暴力也打炸了

    1、归并排序最后没有for(k=l;k<=r;k++) a[k]=res[k];

    2、归并排序后数组变为有序,下一次操作前没有还原

    3、输入n,元素有1<<n个,全用的n

    暴力:

    #include<cstdio>
    #include<iostream>
    #define N 131073
    using namespace std;
    typedef long long LL;
    int a[N],tot,res[N],t[N];
    LL ans;
    inline void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    void reverse(int k)
    {
        int len=1<<k,tmp=len>>1;
        for(int i=1;i<=tot;i+=len)
        {
            for(int j=1;j<=tmp;j++) 
             swap(a[j+i-1],a[len-j+1+i-1]);
        }
    }
    void gbsort(int l,int r)
    {
            if(l==r) return;
            int mid=l+r>>1;
            gbsort(l,mid);
            gbsort(mid+1,r);
            int i=l,j=mid+1,k=l;
            while(i<=mid && j<=r)
            {
                if(a[i]>a[j])  { ans+=mid-i+1; res[k++]=a[j++]; }
                else res[k++]=a[i++];
            }
            while(i<=mid) res[k++]=a[i++];
            while(j<=r) res[k++]=a[j++];
            for(k=l;k<=r;k++) a[k]=res[k];
    }
    void out(long long x)
    {
        if(x/10) out(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
        freopen("pair.in","r",stdin);
        freopen("pair.out","w",stdout);
        int n; read(n);
        tot=1<<n;
        for(int i=1;i<=tot;i++) read(a[i]);
        int q,k;
        read(q);
        while(q--)
        {
            read(k);
            reverse(k);
            for(int i=1;i<=tot;i++) t[i]=a[i];
            ans=0;
            gbsort(1,tot);
            for(int i=1;i<=tot;i++) a[i]=t[i];
            out(ans);
            printf("
    ");
        }
        fclose(stdin); fclose(stdout);
        return 0;
    } 
    View Code
  • 相关阅读:
    完成了一次子网站向顶级网站的迁移
    Sharepoint中子网站迁移到顶级网站的方法
    Chain Of Responsibility——设计模式学习笔记
    短信PDU编码解析
    Decorator模式——设计模式学习笔记
    adb常用命令和logcat使用
    Explicit——谨慎定义隐式类型转换函数
    FlyWeight模式——设计模式学习笔记
    Facade模式——设计模式学习笔记
    宏——#define与#undef
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7638794.html
Copyright © 2020-2023  润新知