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

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

输入样例

输出样例

404 3

10 20 30 40

1 4

1 2

2 3

样例解释

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

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

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

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

贪心,最开始写了个以每个点的删去以后的代价的贪心,结果发现样例都过不了。然后就该了种贪心的方法、、、

对于每个点不删去的话他会对删去其他的点造成它的点权的影响,我们要使代价降到最小,那么我们要先将这个造成影响最大的点删掉,这样在删其他的点时就先把这个代价最大的部分去掉了,这样应该是最优的·

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define LL long long
using namespace std;
bool vis[N];
long long ans;
int n,m,x,y,w[N],sum[N];
vector<int>vec[N];
int read()
{
    ,f=; char ch=getchar();
    ;ch=getchar();}
    +ch-',ch=getchar();
    return x*f;
}
struct Node
{
    int w,num;
}a[N];
int cmp(Node a,Node b){return a.w>b.w;}
int main()
{
    freopen("god.in","r",stdin);
    freopen("god.out","w",stdout);
    n=read(),m=read();
    ;i<=n;i++)
      a[i].w=read(),a[i].num=i,w[i]=a[i].w;
    ;i<=m;i++)
    {
        x=read(),y=read();
        vec[x].push_back(y),vec[y].push_back(x);
     }
    sort(a+,a++n,cmp);
    ;i<=n;i++)
    {
        x=a[i].num;vis[x]=true;
        ;j<vec[x].size();j++)
         if(!vis[vec[x][j]]) ans+=(LL)w[vec[x][j]];
    }
    printf("%I64d",ans);
    ;
}

AC代码

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

啊啊啊,得了40分,然后改了一个小时以后才把它改成50、、、(呜呜)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define maxn 0x7fffffff
using  namespace std;
bool flag;
char a[N];
int q,s,num1,num2,minn=maxn;
int main()
{
    freopen("bit.in","r",stdin);
    freopen("bit.out","w",stdout);
    cin>>a;int l=strlen(a);
    ]==; ;
    ;i>=s;i--)
    {
        '&&minn==maxn)
        {
            ') a[i]++;
            num1=i,minn=a[i];
        }
        if(a[i]>minn)
         ){q=a[i],a[i]=minn,flag=true,a[num1]=q,num2=i; break;}
         else {a[i]++,flag=true,num2=i;break;}
    }
    if(flag)
    {
        ')
        {
            ) ;i<l-num2-;i++) cout<<a[i];
            else
            {
                q=a[num1],a[num1]=a[num2],a[num2]=q;
                ;i<=num1;i++) cout<<a[i];
            }
        }
        ;i<l;i++) cout<<a[i];
    }
    else
     ]=='-')
     {
         ;i<l;i++) cout<<a[i];
         else
         {
             printf("-1");
             ;i<l;i++) cout<<a[i];
         }
     }
     else
     {
         printf("-");
         ;i<l;i++) cout<<a[i];
     }
    ;
}

50分代码(这个代码有很多bug)

分类讨论

分为正数和负数两种情况,当为负数时,最优解一定是倒着数第一个不为9的数+1

当为正数时,又有两种情况,一种是答案为正数,一种是答案为负数。

我们先找到除最后一个数以外第一个(倒着)大于0的数,如果这个数往后的数都大于8并且8的个数小于1个,那么这个数不满足条件,因为如果这样的话我们后面没法使后面的位置总共加上2(因为要价值+1,那么原数的每位数的和就要+1,并且我们找到的这个合法的位置上的数要-1所以后面的数的和要比原数多2),这样我们就继续往前找,找到一个合法的位置,让这个位置上的数-1,前面的数不变,后面的数最高位上尽量放最大的数,但是这个数的和等于sum,如果没有数了就补零

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 100010
using namespace std;
bool flag;
char ch[N];
int t,num[N];
int main()
{
//    freopen("bit.in","r",stdin);
//    freopen("bit.out","w",stdout);
    cin>>ch+;
    ),s=;
    ]=='-')//分类讨论,为负数的情况,这种情况比较好做,我们找到第一个小于9的位置,这个位置上的数+1,然后其余的不变,这就是最小值,
    {
        for(int i=l;i>s;i--)//找到第一个小于9的位置
          ')
            {ch[i]++,flag=true;break;} //+1
        ]=]=;//判断是否在原序列中找到这样的位置,如果不能,我们要在最前面+1
        for(int i=s;i<=l;i++) cout<<ch[i];
    }
    else//正数 ,我们需要找到除最后一个数以外第一个(这里为倒着的)大于零的数,然后使这个-1,这个位置前面的数不变,后面的数按最高位上放最大的,没有可以放的数时补零
    {
         ;i<=l;i++) num[i]=ch[i]-';
         ,s;
         ;i>=;i--)//我们可能会遇到这样的情况例如99,98,998我们在第一个不为零得数上-1后面的数加不开了怎么办,这样的话一定是输出负数,且为在这个数前面多了一个1
         {
             ) //找第一个不为零的数
            {
                ]==||(num[i+]==&&t==)) {]==) t++;sum+=num[i];}//上面的那种情况对于329这样的书就不满足条件了,这样我们要找第二个大于0的数并且这个数往后最小的数一定不为9,8为最小的话至少两个8,然后在进行操作
                else {flag=true,num[i]--,s=i;break;}
             }
             else sum+=num[i];
         }
         if(!flag)
         {
             printf("-");
             )
             {
                 printf(");
                 ;i<=l;i++) cout<<num[i];
              }
             else //当为98这种数时我们的结果为-99
             {
                 num[l]++;
                 ;i<=l;i++) cout<<num[i];
             }
         }
         else
         {
             flag=false;
             while(sum)
            {
                num[++s]=min(sum,);
                sum-=min(sum,);
            }
             ;i<=s;i++)
             ||flag) flag=true,printf("%d",num[i]);
            ;i<=l;i++) printf(");
         }
    }
    ;
}

AC代码

逆序对(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。

还有不知道怎么求逆序对的童鞋吗?http://www.cnblogs.com/z360/p/6921604.html(对逆序对了解不是很清楚的可以看一下这个博客)

又不懂st表的同学吗?http://m.blog.csdn.net/BerryKanry/article/details/70177006(st表讲解)

把1左移m位,相当于1扩大了2^m

(参考博客http://www.cnblogs.com/TheRoadToTheGold/p/7638794.html)

例: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<cstdlib>
#include<iostream>
#include<algorithm>
#define N (1<<17)+10
#define LL long long
using namespace std;
int n,Q,x,tot,a[N],P[N],tmp[N],bit[N];
LL ans,ni[N],sum[N],shun[N],same[N],st[N][],ST[N][];
int read()
{
    ,f=; char ch=getchar();
    ;ch=getchar();}
    +ch-',ch=getchar();
    return x*f;
}
void qsort(int l,int r)//求逆序对,逆序对的求法都会吧?不会的直接去哪个链接里面看吧
{
    if(l==r) return;
    ;
    qsort(l,mid);
    qsort(mid+,r);
    for(int i=r;i>=l;i--) P[i]=i;//p赋初值,P数组表示的是一段连续的区间内相同的数的最大标号。
    ;i>=l;i--)
     ]) P[i]=P[i+];
    ,k=l;
    while(i<=mid&&j<=r)//归并排序,先左边的跟右边的比较大小,在一边的没有了以后在直接把另一边的复制到tmp数组中,tmp数组是我们已经排好序的数组
    {//归并排序是基于二分的思想,先将小的区间合并然后在合并大的区间,也就是说我们在合并一个大的区间的时候他的左右这两段小区间是已经排好序的
        if(a[i]<a[j]) tmp[k++]=a[i++];//前面的数小于后面的数,对逆序对的个数没有影响
        else
         ]]+=P[j]-j+,tmp[k++]=a[i++];
         //统计区间内的数相等的区间,ST数组储存的是以i为开头的,长度为r-l+1的相同的区间,
         //为什么个数为P[j]-j?p[j]为是当前数的最大的在数组内的编号,这一段数的个数为p[j]-j这些数可以和a[i]组成相等的区间
         ]]+=mid-i+,tmp[k++]=a[j++];
         //st数组储存的是以i为开头的,长度为r-l+1的区间内的逆序对的个数,如果a[i]比a[j]大说明i后面的所有的数都会比j大,那么能组成的逆序对的个数即为mid-i+1
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(k=l;k<=r;k++) a[k]=tmp[k];
}
int work()
{
    ;i<=n;i++) sum[i]=1LL*(<<i-)*(<<i-);//sum数组表示长为i的区间的,i小于mid,j大于mid的区间的个数
    ,len=;i<=n;i++,len<<=)//2^i为区间的长度,也就是len
     ;j<=tot;j+=len)//j为区间的头结点
      ni[i]+=st[j][i],shun[i]+=sum[i]-st[j][i],same[i]+=ST[j][i];//ni表示在长度为2^i区间里的逆序对的个数,shun为顺序对的个数,顺序对的个数等于总区间的个数减去逆序对的个数
}
int main()
{
    freopen("pair.in","r",stdin);
    freopen("pair.out","w",stdout);
    n=read(),tot=<<n;
    ;i<=tot;++i) bit[i]=bit[i>>]+;//bit数组储存的是2^bit[i]=i,也就是lg i的大小,为什么要处理一个bit数组?因为我们在后面要用st表记录一段区间内的逆序对的个数,而st表的st[i][j]表示以i为头结点,以2^j为结尾的一段区间
    ;i<=tot;++i) a[i]=read();
    qsort(,tot);work();//求逆序对以及处理逆序对的个数
    Q=read();
    while(Q--)
    {
        x=read();ans=;//总个数-逆序对的个数=顺序对的个数,逆序对的个数=顺序对的个数-相同的个数
        ;i<=x;i++) swap(ni[i],shun[i]),ni[i]-=same[i],shun[i]=sum[i]*tot/(<<i)-ni[i];//对于每一个反转,我们都可以将其拆分成交换相邻的2^(步数-1)
        ;i<=n;i++) ans+=ni[i];//统计答案
        printf("%I64d\n",ans);
    }
    ;
}

AC代码

              

              

                      距 NOIp2017 还剩 29 天

                               你可以做的事情还有很多,即使到最后一秒也不要放弃,因为不到结束的那一刻谁也不知道结果会怎样。

10.1综合强化刷题 Day2 morning的更多相关文章

  1. 10.1综合强化刷题 Day2 afternoon

    最大值(max) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有一本书,上面有很多有趣的OI问题.今天LYK看到了这么一道题目: 这里有一个长度为n的 ...

  2. 10.1综合强化刷题 Day2

    a[问题描述]你是能看到第一题的 friends呢.                                                —— hja世界上没有什么比卖的这 贵弹丸三还令人绝 ...

  3. 10.1综合强化刷题 Day3 morning

    竞赛时间:????年??月??日??:??-??:?? 题目名称 a b c 名称 a b c 输入 a.in b.in c.in 输出 a.out b.out c.out 每个测试点时限 1s 1s ...

  4. 10.1综合强化刷题 Day3 afternoon

    竞赛时间:????年??月??日??:??-??:?? 题目名称 a b c 名称 a b c 输入 a.in b.in c.in 输出 a.out b.out c.out 每个测试点时限 1s 1s ...

  5. 10.1综合强化刷题 Day7

                                                                                       noip提高组模拟赛       ...

  6. 10.1综合强化刷题 Day1 afternoon

    一道图论好题(graph) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有一张无向图G={V,E},这张无向图有n个点m条边组成.并且这是一张带权图, ...

  7. 10.1综合强化刷题 Day6

    T1 排序 题目描述 小Z 有一个数字序列a1; a2; .... ; an,长度为n,小Z 只有一个操作:选 定p(1<p<n),然后把ap 从序列中拿出,然后再插⼊到序列中任意位置. ...

  8. 10.1综合强化刷题 Day5

    T1 拼不出的数 lost.in/.out/.cpp[问题描述]3 个元素的集合{5; 1; 2}的所有子集的和分别是0; 1; 2; 3; 5; 6; 7; 8.发现最小的不能由该集合子集拼出的数字 ...

  9. 10.1综合强化刷题 Day4

    财富(treasure) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有n个小伙伴.每个小伙伴有一个身高hi. 这个游戏是这样的,LYK生活的环境是以 ...

随机推荐

  1. ipv4配置

  2. 使用观察者模式更新Fragment的内容

    最近有个需求,就是在Fragment没有切换的时候(show,hide)更新Fragment显示的内容,想了一会,终于想到可以用观察者模式来解决这个问题的. 定义一个[被观察者(接口)]: publi ...

  3. qt4.8.5 qtwebkit 静态编译 版本

    2013年就编译好了,qtwebkit是最不好编译的了,尤其是静态编译,这儿分享给大家 估计总有人会用得到... 静态库下载地址:http://yunpan.cn/cyyNqrApbVDwq  提取码 ...

  4. android/libs/libammsdk.jar" already exists! 解决方法

    Error: Uh oh!"/work/2016/fengkongbao/.meteor/local/cordova-build/platforms/android/libs/libamms ...

  5. dcpromo(server2012不支持)

    dcpromo 编辑 dcpromo命令是一个“开关”命令.如果Windows 2000 Server计算机是成员服务器,则 运行dcpromo命令会安装活动目录,将其升级为域控制器:如果Window ...

  6. Mac进行一些操作时提醒Operation not permitted的完美解决

    Mac版本10.14.5 ,向下向上都行: 1.关闭mac的安全机制,首先可以在正常模式下,输入 csrutil status 命令,查看mac安全机制是否开启. 2.如果 Protection st ...

  7. java课堂 笔记

  8. ibatis selectKey

    <insert id="insert" parameterClass="A"> <selectKey keyProperty="uu ...

  9. 第一个python自动化程序

    #Author:xiaoxiao from selenium import webdriver def searchSelenium(): #启动浏览器 driver = webdriver.Fire ...

  10. 声卡(Sound Card)基本概念

    声卡 (Sound Card)是实现声音的模拟/数字信号相互转换.信号处理的一种硬件. 声卡的基本功能是把来自话筒.磁带.光盘的原始声音信号加以转换(模数转换或者数模转换),输出到耳机.扬声器.扩音机 ...