zoukankan      html  css  js  c++  java
  • 2016 noip 复赛 day2

    T1 组合数题目

     解:这题好像如果知道一个公式,应该就可以做了吧。。。

    重点在于不知道。。。。。

    程序:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    int a[20000],b[20000],c[2010][2010],f[2010][2010];
    int t,k,n,m;
    int main()
    {
      scanf("%d%d",&t,&k);
      for (int i=1;i<=t;i++)
      {
          scanf("%d%d",&a[i],&b[i]);
          n=max(n,a[i]);
          m=max(m,b[i]);
      }
      for (int i=0;i<=2009;i++)
       for (int j=0;j<=2009;j++)
       c[i][j]=-1;
      for (int i=0;i<=n;i++) c[i][1]=i%k;
      for (int i=0;i<=n;i++) c[i][i]=1%k;
      for (int i=1;i<=n;i++)
       for (int j=2;j<=min(m,i);j++)
       if (i!=j)
           c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
      for (int i=1;i<=n;i++)
       for (int j=1;j<=m;j++)
       {
        f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
        if (c[i][j]==0) f[i][j]++;
       }
      for (int i=1;i<=t;i++)
      if ((a[i]==0)||(b[i]==0)) printf("0
    ");
      else printf("%d
    ",f[a[i]][min(a[i],b[i])]); 
      return 0;

    T2 蚯蚓 

      这题第一个想法就是用堆来储存,每次都挑出最大的那只,然后切,再放回去。为了解决每只蚯蚓每秒都会增长,放入堆的时候需减去总共已增长的长度,拿出的时候再加上,就解决了这个问题。但是这样写的话,我就只写了55分。。。。

      然后考虑就会发现,我们把当前的蚯蚓切成了t1和t2,因为蚯蚓每秒都在增长,所以保证t1一定大于下一次被切出的t1,t2一定大于下一次被切出的t2,只是我们不知道各个t1和t2间的大小关系罢了,那么我们就来维护三个队列,开始时,第一个队列为蚯蚓初始长度,并且由大到小排列,第二第三都为空,我们每次选取三个队首中最长的那条蚯蚓进行切分,并把得到的分开的两条蚯蚓分别放回第二和第三队列,反正第二个队列与第三个队列的单调性并不需要去维护就可以保持,这样就免除了之前每次都要logn维护,这样做m次操作。在最后把三个队列合并(切记,不能为了省事就把它们放一个数组里面后用快排,因为三个队列中都是各自有序的,这会导致快排退化,我就因为懒这么做了,然后。。。。还是老老实实两个两个合并吧),然后输出就好了。。

    不过在洛谷上是对了,可是在bzoj上,是Presentation_Error,意思是答案基本正确,但有一些细节错误,总之就是一个多空格或者多空行的错误,然而我找不到我哪儿多了。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        char c;
        while((c<'0')||(c>'9')) c=getchar();
        int ef = 0;
        while((c>='0')&&(c<='9'))
        {
          ef=ef*10+c-48;
          c=getchar();
        }
        return ef;
    }
    int t1,t2,t3,n,m,l,t,x,sum,u,v,h1,h2,h3,tot;
    long long ans;
    int q1[8000001],q2[8000001],q3[8000001],b[8000001];
    bool cmp(const int x,const int y) {return (x>y);}
    void find1()
    {
        int maxx=-2147438646;
        int tx=0;
        if ((q1[h1]>maxx)&&(h1<=t1)){maxx=q1[h1]; tx=1;}
        if ((q2[h2]>maxx)&&(h2<=t2)){maxx=q2[h2]; tx=2;}
        if ((q3[h3]>maxx)&&(h3<=t3)){maxx=q3[h3]; tx=3;}
        if (tx==1) h1++;
        else if (tx==2) h2++;
        else if (tx==3) h3++;
        ans=maxx;
    }
    int main()
    {
      n=read();m=read();l=read();u=read();v=read();t=read();
      h1=h2=h3=1; 
      t1=n;t2=t3=0;
      for (int i=1;i<=n;i++) q1[i]=read();
      sort(q1+1,q1+n+1,cmp);
      sum=0;
      int now2,now,now1;
      tot=0;
      int tu=t;
      for (int i=1;i<=m;i++)
      {
          find1();
          ans=ans+tot;
          now=(int)(ans*u/v);
          tot+=l;
          if ((i==tu)&&(tu+t<=m)) {printf("%lld ",ans);tu+=t;}
          else if (i==tu) {printf("%lld",ans);tu+=t;}
          q2[++t2]=now-tot;
          q3[++t3]=ans-now-tot;
      }
      int sum=0;
      cout<<endl;
      tu=t;
      while ((h1<=t1)||(h2<=t2))
      {
          if ((h1<=t1)&&(h2<=t2))
          {
            sum++;
            if (q1[h1]>q2[h2]){b[sum]=q1[h1];h1++;}
            else {b[sum]=q2[h2];h2++;}
        }
        else 
        {
            sum++;
            if (h2<=t2) {b[sum]=q2[h2];h2++;}
            else {b[sum]=q1[h1];h1++;}
        }
      }
      for (int i=1;i<=sum;i++) q2[i]=b[i];
      h2=1;t2=sum;
      sum=0;
      while ((h3<=t3)||(h2<=t2))
      {
          if ((h3<=t3)&&(h2<=t2))
          {
            sum++;
            if (q3[h3]>q2[h2]){b[sum]=q3[h3];h3++;}
            else {b[sum]=q2[h2];h2++;}
        }
        else 
        {
            sum++;
            if (h2<=t2) {b[sum]=q2[h2];h2++;}
            else {b[sum]=q3[h3];h3++;}
        }
      }
      
      for (int i=1;i<=sum;i++) 
      if ((i==tu)&&(tu+t<=sum)) {printf("%d ",b[i]+tot);tu+=t;}
      else if (i==tu) {printf("%d",b[i]+tot);tu+=t;}
      return 0;
    }

    T3 愤怒的小鸟

    原谅我在做这道题之前没有做过状态压缩dp,总之就是用f[i]来表示在i的二进制表达下,打掉那些表示为1的小猪最少需几只鸟,比如说i为5,即二进制为101是,f[5]表示的是打掉第一只猪和第三只猪最少需要几只鸟。

    具体的一些解释就在程序里讲好了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    double x[30],y[30];
    int t,n,m,f[300000],bird[20][20];
    double eps=1e-9;
    struct per{
        double a,b;
    };
    per calc(int i,int j)//这个纸上写写就好了。。。
    {
      double x1=x[i],y1=y[i],x2=x[j],y2=y[j];
      per now;
      now.a=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1);
      now.b=(y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x1*x1*x2);
      return now;
    }
    int check(double xx)//判等,因为==的精度太高。。。
    {
      if (abs(xx)<=eps) return 0;
      else return 1;
    }
    int main()
    {
      scanf("%d",&t);
      for (int pop=1;pop<=t;pop++)
      {
          scanf("%d%d",&n,&m);
          for (int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
           //首先输入每只猪的坐标
          for (int i=0;i<=299999;i++) f[i]=2100000000;
          for (int i=1;i<=19;i++)
           for (int j=1;j<=19;j++)
           bird[i][j]=0;
    //bird[i][j]表示同时经过(0,0),(xi,yi),(xj,yj)
    //这三个点的二次函数能经过哪些点?首先如果存在这个函数的话,把这个数化为
    二进制,它的第i位和第j位一定是1,表示它能经过这两个点,如果有一只猪(xk,yk)
    也存在于这个二次函数上的话,那么这个数的第k位也就为1,不经过的话对应的位数就为零,
    所以这个数表示的是经过这两点的函数能经过哪几只猪。
    for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) { per sum=calc(i,j); //计算同时经过(0,0),(xi,yi),(xj,yj)这个二次函数对应的a和b; if (sum.a>=0) continue; //如果开口朝上就不存在抛物线了,所以除去 for (int k=1;k<=n;k++) if (check(sum.a*x[k]*x[k]+sum.b*x[k]-y[k])==0) bird[i][j]=bird[i][j]|(1<<(k-1)); //看看有哪些猪是位于这条函数上的,如果位于,就把对应的二进制位数变为零 } f[0]=0; int tot=1<<n; for (int i=0;i<=tot-1;i++)//枚举当前状态,即达到i的二进制这种情况下,至少需要几只小鸟。 for (int j=1;j<=n;j++) if ((i&(1<<(j-1)))==0)//找到标号最前面的那只还未被打掉的小猪。 { for(int k=j+1;k<=n;k++) //枚举它之后的还没被打掉的小猪 { if ((i&(1<<(k-1)))==0) f[i|bird[j][k]]=min(f[i|bird[j][k]],f[i]+1); //看看打掉这两只小猪以及与他们两位于同一抛物线上的小猪,和原来i枚举的那些小猪的最小步数能不能被更新 } f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+1); //有可能只有j一个没被打掉,所以还要考虑这种情况 break; //如果这个程序不加这个break,就会因超时失去三十分。。。
    但很明显一眼看上去会认为这是不对的,因为它会少枚举很多的情况,
    结果我们会发现j枚举的是i状态下编号最早的那只没被打的猪,你会发
    现反正我们早晚都要消灭它,不如在最早的时候消灭好了,何必空着去
    消灭后面的呢?(之后还是要回来消灭它)利用贪心原理,所以我们只
    需找到第一个j就可以了,把第一只没被打的打掉。
    } printf("%d ",f[(1<<n)-1]); } return 0; }
  • 相关阅读:
    [saiku] 系统登录成功后查询Cubes
    216. Combination Sum III
    215. Kth Largest Element in an Array
    214. Shortest Palindrome
    213. House Robber II
    212. Word Search II
    211. Add and Search Word
    210. Course Schedule II
    分硬币问题
    开始学习Python
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6476593.html
Copyright © 2011-2022 走看看