zoukankan      html  css  js  c++  java
  • 国庆 day 2 上午

    一道图论神题(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

     思路:贪心,一遍AC。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #define MAXN 101000
    using namespace std;
    map<int,int>ma[MAXN];
    int n,m,tot,cap[MAXN];
    long long ans;
    int to[MAXN*2],net[MAXN*2],head[MAXN*2];
    struct nond{
        int val,id;
    }v[MAXN];
    int cmp(nond a,nond b){
        return a.val>b.val;
    }
    void add(int u,int v){
        to[++tot]=v;net[tot]=head[u];head[u]=tot;
        to[++tot]=u;net[tot]=head[v];head[v]=tot;
    }
    int main(){
        freopen("god.in","r",stdin);
        freopen("god.out","w",stdout);
         scanf("%d%d",&n,&m);
         for(int i=1;i<=n;i++){
             scanf("%d",&v[i].val);
             v[i].id=i;
             cap[i]=v[i].val;
         }
        sort(v+1,v+1+n,cmp);
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            ma[u][v]=1;
            ma[v][u]=1;
        }
        for(int i=1;i<=n;i++){
            for(int j=head[v[i].id];j;j=net[j])
                if(ma[v[i].id].find(to[j])!=ma[v[i].id].end()){
                    ans+=cap[to[j]];
                    ma[to[j]].erase(v[i].id);
                }
        }
        cout<<ans;
    }
    /*
    4 3
    10 20 30 40
    1 4
    1 2
    2 3
    */
    View Code

    位运算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。

     思路:大模拟,有三种情况没有考虑,挂掉了3个点。

    ~~~~(>_<)~~~~ 改不过来了90分代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    char n[100100];
    int len;
    int num[100100],tmp[100100],ans[100100];
    int main(){
        freopen("bit.in","r",stdin);
        freopen("bit.out","w",stdout);
        int cnt=0,flag=0,flag1=0;
        scanf("%s",n);
        len=strlen(n);
        if(n[0]!='-'){
            for(int i=0;i<len;i++){
                num[i+1]=n[i]-'0';
                tmp[i+1]=num[i+1];
            }
            if(len!=1){
                if(num[len]<=7&&num[len-1]>=1){
                    int y=0;
                    num[len-1]-=1;num[len]+=2;
                    while(num[y]==0)    y++;
                    for(int i=y;i<=len;i++)    cout<<num[i];
                    return 0;
                }
                else if(num[len]>7&&num[len-1]>=1){
                    if(num[len-2]!=0){
                        int bns=9-num[len-1]-2,y=1;
                        num[len-2]-=1;
                        num[len-1]=9;
                        num[len]-=bns;
                        while(num[y]==0)    y++;
                        for(int i=y;i<=len;i++)    ans[++cnt]=num[i];
                    }
                    else{
                        int x=2,y=1;
                        int bns=9-num[len-1]-2; 
                        while(!num[len-x]&&len!=2)    x++;
                        num[len-x]-=1;
                        num[len-x+1]=9;
                        num[len-x+2]=num[len]-bns;
                        while(num[y]==0)    y++;
                        for(int i=y;i<=len-x+2;i++)    ans[++cnt]=num[i];
                        for(int i=len-x+3;i<=len;i++)    ans[++cnt]=0;
                    }
                    for(int i=1;i<=cnt;i++)
                        if(ans[i]>=10||ans[i]<0)    flag=1;
                    if(len==cnt)
                        for(int i=1;i<=cnt;i++)
                            if(ans[i]>tmp[i]){
                                flag=1;
                                break;
                            }
                            else if(ans[i]<tmp[i])
                                break;
                    if(flag){
                        cout<<"-";
                        for(int i=len;i>=1;i--)
                            if(tmp[i]!=9){
                                flag1=1;
                                tmp[i]+=1;
                                break;
                            }
                        if(!flag1)    tmp[0]+=1;
                    }
                    if(!flag)    for(int i=1;i<=len;i++)    cout<<ans[i];
                    if(flag&&flag1)    for(int i=1;i<=len;i++)    cout<<tmp[i];
                    else if(flag&&!flag)    for(int i=0;i<=len;i++)    cout<<tmp[i];
                    return 0;
                }
                else if(num[len]<=7&&num[len-1]<1){
                    int x=1,y=1;
                    while(!num[len-x])    x++;
                    num[len-x]-=1;
                    num[len-x+1]=num[len]+2;
                    while(num[y]==0)    y++;
                    for(int i=1;i<=len-x+1;i++)    cout<<num[i];
                    for(int i=len-x+2;i<=len;i++) cout<<"0";
                    return 0;
                }
            }
            else{
                cout<<"-"<<num[1]+1;
                return 0;
            }
        }
        else{
            len-=1;
            for(int i=1;i<=len;i++){
                num[i]=n[i]-'0';
                tmp[i]=num[i];
            }
            for(int i=len;i>=1;i--)
                if(tmp[i]!=9){
                    flag1=1;
                    tmp[i]+=1;
                    break;
                }
            if(!flag1)    tmp[0]+=1;
            cout<<"-";
            if(flag1)    for(int i=1;i<=len;i++)    cout<<tmp[i];
            else for(int i=0;i<=len;i++)    cout<<tmp[i];
            return 0;
        }
    }
    View Code

    这是标程:

    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <cstring>
    using namespace std;
    char s[100005];
    int len,sum,i,j,FLAG;
    int main()
    {
        freopen("bit.in","r",stdin);
        freopen("bit.out","w",stdout);
            scanf("%s",s); FLAG=0;
            if (s[0]!='-')
            {
            len=strlen(s); sum=0;
            for (i=len-1; i>=0; i--)
            {
                if (s[i]>='1' && sum>=2)
                {
                    s[i]=char(s[i]-1);
                    sum=2;
                    for (j=i+1; j<len; j++)
                    {
                        while (sum && s[j]<'9')
                        {
                            sum--;
                            s[j]=char(s[j]+1);
                        }
                    }
                    int SS=0;
                    for (j=i+1; j<len; j++) SS+=s[j]-'0';
                    for (j=i+1; j<len; j++)
                    {
                        if (SS>=9) {s[j]='9'; SS-=9;} else {s[j]=char(SS+'0'); SS=0;}
                    }
                    FLAG=1;
                    break;
                }
                sum+='9'-s[i];
            }
            if (FLAG)
            {
                for (i=0; i<len; i++) if (s[i]!='0') break;
                for (j=min(len-1,i); j<len; j++) cout<<s[j]; cout<<endl;
                return 0;
            }
            cout<<'-';
            for (i=len-1; i>=0; i--)
            {
                if (s[i]<'9')
                {
                    s[i]=char(s[i]+1);
                    FLAG=1;
                    break;
                }
            }
            if (FLAG)
            {
                for (i=0; i<len; i++) if (s[i]!='0') break;
                for (j=min(len-1,i); j<len; j++) cout<<s[j]; cout<<endl;
                return 0;
            }
            cout<<1;
                for (i=0; i<len; i++) if (s[i]!='0') break;
                for (j=min(len-1,i); j<len; j++) cout<<s[j]; cout<<endl;
            return 0;
            }
    
            FLAG=0;
            cout<<'-';
            len=strlen(s);
            for (i=len-1; i>=1; i--)
            {
                if (s[i]<'9')
                {
                    s[i]=char(s[i]+1);
                    FLAG=1;
                    break;
                }
            }
            if (FLAG)
            {
                for (i=1; i<len; i++) if (s[i]!='0') break;
                for (j=min(len-1,i); j<len; j++) cout<<s[j]; cout<<endl;
                return 0;
            }
            cout<<1;
                for (i=1; i<len; i++) if (s[i]!='0') break;
                for (j=min(len-1,i); j<len; j++) cout<<s[j]; cout<<endl;
        return 0;
    }
    View Code

    逆序对(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<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=(1<<17)+5;
    int n,T,PP,now,A,sum,o;
    int p[N],a[N],b[N],RR[N];
    long long t[105],TT[105];
    long long st[N][20],ST[N][20];
    void pre(int l,int r){
           if(l==r)    return;
        int    mid=(l+r)/2;
        pre(l,mid);
        pre(mid+1,r);
        int i=l,j=mid+1,o=l;
        for(i=l;i<=r;i++)    b[i]=a[i];
        for(i=r;i>=mid;i--){
            RR[i]=i;
            if(i!=r&&b[i+1]==b[i])
                RR[i]=RR[i+1];
        }
        i=l;j=mid+1;
        while(i<=mid&&j<=r){
            if(b[i]<=b[j]){
                a[o++]=b[i];
                if(b[i]==b[j])
                    ST[l][p[r-l+1]]+=RR[j]-j+1;
                i++;
            }
            else{
                a[o++]=b[j];
                st[l][p[r-l+1]]+=mid-i+1;
                j++;
            }
        }
        if(i<=mid)
            for(j=i;j<=mid;j++)    a[o++]=b[j];
        else    if(j<=r)
            for(i=j;i<=r;i++)    a[o++]=b[i];
    }
    int main(){
        freopen("pair.in","r",stdin);
        freopen("pair.out","w",stdout);
        scanf("%d",&n);
        sum=(1<<n);
        for(int i=1;i<=sum;i++)    scanf("%d",&a[i]);
        for(int i=1;i<=17;i++)    p[1<<i]=i;
        pre(1,sum);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=(1<<n);j+=(1<<i)){
                t[i]+=st[j][i];
                TT[i]+=ST[j][i];
              }
        scanf("%d",&T);
        while(T--){
            long long ans=0;int Q;
            scanf("%d",&Q);
            for(int i=1;i<=Q;i++) t[i]=1ll*(1<<i-1)*(1<<i-1)*(1<<n-i)-TT[i]-t[i];
            for(int i=1;i<=n;i++) ans+=t[i];
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    View Code
    细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
  • 相关阅读:
    docker 安装 clickhouse单机版
    CockRoachDB简介
    Ubuntu18.04 LTS Cockroach集群搭建
    ClickHouse 的一些优化参数
    ClickHouse 概念整理
    OOM Killer机制
    win10系统下载地址
    Quartz.Net在C#中的使用
    JavaScript的undefined与null、NaN的区别
    Java Web基础回顾 —JSP
  • 原文地址:https://www.cnblogs.com/cangT-Tlan/p/7639942.html
Copyright © 2011-2022 走看看