NOIP2017提高组 模拟赛16(总结)

来源:互联网 发布:360解压软件下载 编辑:程序博客网 时间:2024/06/05 02:49

NOIP2017提高组 模拟赛16(总结)

第一题 比赛 (原题 noip2011 瑞士轮)

【题目描述】

【解题思路】

  每一次比赛过后,赢的N名选手是有序的,输的N名选手也是有序的(赢的+1,赢的那N个都+1,对位置不会有影响。输的也是一样)。直接用O(N)的时间归并即可。
  时间复杂度:O(N logN+RN)

【代码】

#include<cstdio>#include<algorithm>using namespace std;typedef long long ll;const int N=200100;int n,t,ned;int D[N],q1[N],q2[N],p1,p2;struct data { int g,p,id; } d[N];bool cmp(int A,int B) { return (d[A].g>d[B].g || (d[A].g==d[B].g && A<B)); }int main(){    freopen("2241.in","r",stdin);    freopen("2241.out","w",stdout);    scanf("%d%d%d",&n,&t,&ned);    n<<=1;    for(int i=1;i<=n;i++) scanf("%d",&d[i].g);    for(int i=1;i<=n;i++) scanf("%d",&d[i].p),D[i]=i;    sort(D+1,D+1+n,cmp);    while(t--)    {        p1=0; p2=0;        for(int i=1;i<=n;i+=2)        if(d[D[i]].p>d[D[i+1]].p)        {            d[D[i]].g++;            q1[++p1]=D[i]; q2[++p2]=D[i+1];        } else        {            d[D[i+1]].g++;            q1[++p1]=D[i+1]; q2[++p2]=D[i];        }        p1=p2=1;        for(int i=1;i<=n;i++)        {            if(p1<=(n>>1))            {                if(d[q1[p1]].g>d[q2[p2]].g || (d[q1[p1]].g==d[q2[p2]].g && q1[p1]<q2[p2]))                D[i]=q1[p1++]; else D[i]=q2[p2++];            } else D[i]=q2[p2++];        }    }    printf("%d\n",D[ned]);    return 0;}

第二题 奶牛跑步

【题目描述】

【解题思路】

  比赛持续了T=C*L/speed_max
  奶牛i超过奶牛j的次数Pij=T*(speed_i-speed_j)/C=L/speed_max*(speed_i-speed_j),Pij要向下取整。
  由于L/speed_max是小数,不好搞。
  可以分开商和余数来做,先忽略掉余数(只考虑整数部分)
  例如3.2-1.5就变成3-1=2。
  那么这样是会算多的,因为0.2-0.5<0,必须答案-1。
  用树状数组存余数就可以解决这一问题。
  speed_max≤1000000,余数是存的下的。
  (速度从小到大排序)

【代码】

#include<cstdio>#include<algorithm>using namespace std;typedef long long ll;const int N=100100;int n,len,sp[N];ll T[1001000],ans,last,m,now;void add(int x) { for(int i=x;i<=sp[n];i+=(i&-i)) T[i]++; }ll query(int x){    ll yu=0ll;    for(int i=x;i;i-=(i&-i)) yu+=T[i];    return yu;}bool cmp(int A,int B) { return (A<B); }int main(){    freopen("2242.in","r",stdin);    freopen("2242.out","w",stdout);    scanf("%d%lld%d",&n,&m,&len);    for(int i=1;i<=n;i++) scanf("%d",&sp[i]);    sort(sp+1,sp+1+n,cmp);    last=0ll; ans=0ll;    for(int i=1;i<=n;i++)    {        ll q=m*sp[i]/sp[n];        ll p=m*sp[i]%sp[n];        last+=q;        now=q*i-last;        add(p+1);        now-=(i-query(p+1));        ans+=now;    }    printf("%lld\n",ans);    return 0;}

第三题 T-shirt (cf183d)

【题目描述】

【解题思路】

50%
  假设至少x人实际衬衫是k的概率是poss[k][x]
  假设第1到第y个人中至少x人实际衬衫是k的概率是f[k][y][x]
  f[k][y][x]=f[k][y-1][x-1] * pyk + f[k][y-1][x] * (1-pyk)
  Poss[k][x] = f[k][n][x]
  如果买x件衬衫,期望匹配数是sigma poss[k]i
  可以发现,k一定时,poss[k]单调减小。而假设原来有x件k号衬衫,现在多买一件k号衬衫,那么第x+1件k号衬衫对答案的贡献是poss[k][x+1]
  可以发现,答案是poss数组中,最大的n个数的和
  时间复杂度O(N*N*M)

100%
  利用单调性优化:如果poss[k][x]还未被选取,则必然不会选取poss[k][x+1].因此我们可以在选取了poss[k][x]后再计算poss[k][x+1]的值。
  时间复杂度O(N*N+N*M)

【代码】

#include<cstdio>#include<algorithm>#define imax(a,b) (((a-b)>eps)?(a):(b))using namespace std;typedef long long ll;const double eps=1e-6;const int N=3010;const int M=310;int n,m,g[M];double fn[2][M][N],p[N][M],poss[M][2],ans;void letplay(int cl){    int yu=g[cl]&1^1;    poss[cl][yu]=0.0;    for(int i=1;i<=n;i++)    fn[yu][cl][i]=fn[yu^1][cl][i-1]*p[i][cl]+fn[yu][cl][i-1]*(1.0-p[i][cl]);    for(int i=0;i<=n;i++) fn[yu^1][cl][i]=0.0;    poss[cl][yu]=fn[yu][cl][n];    g[cl]^=1;}int main(){    freopen("2243.in","r",stdin);    freopen("2243.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    for(int j=1;j<=m;j++)    {        int a; scanf("%d",&a);        p[i][j]=1.0*a/1000;    }    for(int i=1;i<=m;i++)    {        g[i]=0;        for(int j=0;j<=n;j++) fn[0][i][j]=1.0;        letplay(i);    }    poss[0][0]=0.0; g[0]=0; ans=0.0;    for(int i=1;i<=n;i++)    {        int mx=0;        for(int j=1;j<=m;j++)        if(poss[j][g[j]&1]>poss[mx][g[mx]&1]) mx=j;        ans+=poss[mx][g[mx]&1];        letplay(mx);    }    printf("%.8lf\n",ans);    return 0;}
原创粉丝点击