zoukankan      html  css  js  c++  java
  • 2017-10-02-morning

    T1 一道图论神题(god)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

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

    LYK想把这个图删干净,它的方法是这样的。每次选择一个点,将它删掉,但删这个点是需要代价的。假设与这个点相连的还没被删掉的点是u1,u2,…,uk。LYK将会增加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

     1 /*
     2 对于一条边的两个端点,肯定是选权值大最优 
     3 然后对于每条边,都采取这种策略就完成了 
     4 */
     5 #include <algorithm>
     6 #include <cstdio>
     7 
     8 inline void read(int &x)
     9 {
    10     x=0; register char ch=getchar();
    11     for(; ch>'9'||ch<'0'; ) ch=getchar();
    12     for(; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
    13 }
    14 const int N(100005);
    15 int n,m,val[N];
    16 long long ans;
    17 
    18 int Presist()
    19 {
    20     freopen("god.in","r",stdin);
    21     freopen("god.out","w",stdout);
    22     read(n),read(m);
    23     for(int i=1; i<=n; ++i)    read(val[i]);
    24     for(int u,v,i=1; i<=m; ++i)
    25     {
    26         read(u),read(v);
    27         ans+=(long long)std::min(val[u],val[v]);
    28     }
    29     printf("%I64d",ans);
    30     return 0;
    31 }
    32 
    33 int Aptal=Presist();
    34 int main(int argc,char**){;}
    贪心AC

     

    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。

    对于负数的情况从后往前找第一个不是9的数++

     1 #include <cstring>
     2 #include <cstdio>
     3 
     4 inline void read(int &x)
     5 {
     6     x=0; register char ch=getchar();
     7     for(; ch>'9'||ch<'0'; ) ch=getchar();
     8     for(; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
     9 }
    10 char s[100005];
    11 int n,num[100005];
    12 
    13 inline void work1()
    14 {
    15     int i,flag=0;
    16     for(i=2; i<=n; ++i) num[i]=s[i]-'0';
    17     for(i=n; i>1; --i)
    18         if(num[i]!=9)
    19         {
    20             num[i]++;
    21             flag=1;
    22             break;
    23         }
    24     printf("-");
    25     if(!flag) printf("1");
    26     for(int i=2; i<=n; ++i)    printf("%d",num[i]);
    27 }
    28 
    29 int K;
    30 inline void work2()
    31 {
    32     for(int i=1; i<=n; ++i) num[i]=s[i]-'0',K+=num[i];
    33     if(num[1]==1&&num[2]==0&&n==2) { printf("%d
    ",2); return ; }
    34     if(++K<10&&n==2&&num[1]==1) { printf("%d
    ",K); return ; }
    35 }
    36 
    37 int Presist()
    38 {
    39 //    freopen("bit.in","r",stdin);
    40 //    freopen("bit.out","w",stdout);
    41     scanf("%s",s+1);n=strlen(s+1);
    42     if(s[1]=='-') work1();
    43     else work2();
    44     return 0;
    45 }
    46 
    47 int Aptal=Presist();
    48 int main(int argc,char**){;}
    考试只做了负数和特殊正数的情况60分

     对于正数的情况,

    ①设当前位置i到最后一位的价值为tot,如果tot<=(n-i)*9+num[i]-1

    最优解为1~i-1为原数,i+1~n尽量填最大,num[i]--;

    ②否则,为绝对数值最小的负数,等价于位数最小的负数,第一位之后填尽量多的9

     1 #include <cstring>
     2 #include <cstdio>
     3 
     4 inline void read(int &x)
     5 {
     6     x=0; register char ch=getchar();
     7     for(; ch>'9'||ch<'0'; ) ch=getchar();
     8     for(; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
     9 }
    10 const int N(100005);
    11 int n,num[N],k[N],K;
    12 char s[N];
    13 
    14 inline void work1()
    15 {
    16     int i,flag=0;
    17     for(i=2; i<=n; ++i) num[i]=s[i]-'0';
    18     for(i=n; i>1; --i)
    19         if(num[i]!=9)
    20         {
    21             num[i]++;
    22             flag=1;
    23             break;
    24         }
    25     printf("-");
    26     if(!flag) printf("1");
    27     for(int i=2; i<=n; ++i)    printf("%d",num[i]);
    28 }
    29 
    30 inline void work2()
    31 {
    32     for(int i=1; i<=n; ++i) num[i]=s[i]-'0',K+=num[i];
    33     int tot=num[n]+1;
    34     for(int i=n-1; i>=1; --i)
    35     {
    36         tot+=num[i];
    37         if(!num[i]) continue;
    38         if(tot<=num[i]-1+(n-i)*9)
    39         {
    40             num[i]--;tot-=num[i];
    41             if(num[1]) printf("%d",num[1]);
    42             for(int j=2; j<=i; ++j) printf("%d",num[j]);
    43             for(int j=i+1; j<=n; ++j)
    44                 if(tot>=9) printf("9"),tot-=9;
    45                 else printf("%d",tot),tot=0;
    46             return ;
    47         }
    48     }
    49     printf("-"); K++; int len=K/9;
    50     if(K%9) printf("%d",K%9);
    51     for(; len--; ) printf("9");
    52 }
    53 
    54 int Presist()
    55 {
    56 //    freopen("bit.in","r",stdin);
    57 //    freopen("bit.out","w",stdout);
    58     scanf("%s",s+1);n=strlen(s+1);
    59     if(s[1]=='-') work1();
    60     else work2();
    61     return 0;
    62 }
    63 
    64 int Aptal=Presist();
    65 int main(int argc,char**){;}
    AC

    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<=10,Q<=10。

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

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

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

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

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

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

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

    在这里顺序对个数是用总个数-逆序对个数求的,要注意相等的情况

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

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

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

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

     1 #include <cstdio>
     2 
     3 #define LL long long
     4 #define swap(a,b) {LL c=a;a=b;b=c;}
     5 
     6 inline void read(int &x)
     7 {
     8     x=0; register char ch=getchar();
     9     for(; ch>'9'||ch<'0'; ) ch=getchar();
    10     for(; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
    11 }
    12 const int N(1<<17);
    13 int n,nn,m,a[N+5],log2[N+5];
    14 
    15 int last[N+5],order[N+5];
    16 LL st[N+5][18],euq[N+5][18];
    17 LL same[N+5],seq[N+5],rev[N+5],sum[N+5];
    18 void Merge_sort(int l,int r)
    19 {
    20     if(l==r) return ;
    21     int mid=l+r>>1;
    22     Merge_sort(l,mid);
    23     Merge_sort(mid+1,r);
    24     int i,j,k,len=log2[r-l+1];
    25     for(i=l; i<=r; ++i) last[i]=i;
    26     for(i=r; i>l; --i)
    27         if(a[i]==a[i-1]) last[i-1]=last[i];
    28     for(i=k=l,j=mid+1; i<=mid&&j<=r; )
    29         if(a[i]==a[j]) euq[l][len]+=last[j]-j+1,order[k++]=a[i++];
    30         else if(a[i]>a[j]) st[l][len]+=mid-i+1,order[k++]=a[j++];
    31              else order[k++]=a[i++];
    32     for(; i<=mid; ) order[k++]=a[i++];
    33     for(; j<=r; ) order[k++]=a[j++];
    34     for(i=l; i<=r; ++i) a[i]=order[i];
    35 }
    36 
    37 inline void Prepare()
    38 {
    39     for(int i=2; i<=nn; ++i) log2[i]=log2[i>>1]+1;
    40     Merge_sort(1,nn);
    41     for(int i=1; i<=n; ++i) sum[i]=1ll*(1<<i-1)*(1<<i-1);
    42     for(int len=2,i=1; i<=n; ++i,len<<=1)
    43       for(int j=1; j<=nn; j+=len)
    44       {
    45         rev[i]+=st[j][i],
    46         same[i]+=euq[j][i],
    47         seq[i]+=sum[i]-st[j][i];
    48       }
    49 }
    50 
    51 int Presist()
    52 {
    53 //    freopen("pair.in","r",stdin);
    54 //    freopen("pair.out","w",stdout);
    55     read(n),nn=1<<n;
    56     for(int i=1; i<=nn; ++i) read(a[i]);
    57     Prepare(),read(m);
    58     for(int k; m--; )
    59     {
    60         read(k);long long ans=0;
    61         for(int i=1; i<=k; ++i)
    62         {
    63             swap(rev[i],seq[i]);
    64             rev[i]-=same[i];
    65             seq[i]=sum[i]*nn/(1<<i)-rev[i];
    66         }
    67         for(int i=1; i<=n; ++i) ans+=rev[i];
    68         printf("%I64d
    ",ans);
    69     }
    70     return 0;
    71 }
    72 
    73 int Aptal=Presist();
    74 int main(int argc,char**argv){;}
    AC
    ——每当你想要放弃的时候,就想想是为了什么才一路坚持到现在。
  • 相关阅读:
    LeetCode "Palindrome Partition II"
    LeetCode "Longest Substring Without Repeating Characters"
    LeetCode "Wildcard Matching"
    LeetCode "Best Time to Buy and Sell Stock II"
    LeetCodeEPI "Best Time to Buy and Sell Stock"
    LeetCode "Substring with Concatenation of All Words"
    LeetCode "Word Break II"
    LeetCode "Word Break"
    Some thoughts..
    LeetCode "Longest Valid Parentheses"
  • 原文地址:https://www.cnblogs.com/Shy-key/p/7652451.html
Copyright © 2011-2022 走看看