• 线段树专辑 —— pku 3145 Harmony Forever


    http://poj.org/problem?id=3145

    Harmony Forever。。。多吹牛B啊。

    这题巧妙的运用了鸽笼原理,什么是鸽笼原理?鸽笼原理就是给你N+1个数,则必定至少有两个数的余数是相同的!

    解决这题的时候,我们先用RMQ的方法求出每一段的最小值,这是容易做到的。

    这题的数据范围是500000,也就是说最多会有500000个叶子节点,当该叶子节点的值为inf的时候,表示这里没有值。当它的值等于它的位置的时候,表示它有值,例如:

    集合[3,4,5]在线段树中叶子节点中的表示为 [inf,inf,inf,3,4,5],前面三个inf分别表示0位,1位和2位

    询问的时候,假设我们要求的MOD是Y,则首先查找[0,Y-1]区间的最小值,因为这样的区间不会有两个数的余数相同,记录下结果。然后依次查找[Y,Y+Y-1],[Y+Y,Y+Y+Y-1]。。。。等区间,每个区间都会找出一个最小值,我们将这些最小值对Y进行取模,得到最小值!

    还是看上面的例子,假如现在MOD是4.

    根据上面的方法,我们便先查找[0,3],得到最小值是3,3%4==3,这是目前的答案。

    接下来查找区间[4,5],得到最小值4,4%4==0,这便是最新的答案!

    从这个例子中,我们也可以看到鸽笼原理的正确性,不过不对的话,那么假如我们首先查找区间[0,4],那么最小值是3,再查找区间[5,5],最小值是5,这样的话,最优的4就被我们丢了!

    关于鸽笼原理更多知识,看组合数学吧!

    View Code
      1 #include<iostream>
    2 #include<string>
    3 #include<cmath>
    4 #include<algorithm>
    5 using namespace std;
    6 #define inf 500005
    7
    8 struct node
    9 {
    10 int l;
    11 int r;
    12 int min_val;
    13 };
    14
    15 node tree[2500000];
    16 int n,len;
    17 int pos[inf];
    18 int val[inf];
    19
    20 int min(int a,int b)
    21 {
    22 return a<b?a:b;
    23 }
    24
    25 void build(int i,int l,int r)
    26 {
    27 tree[i].l=l;
    28 tree[i].r=r;
    29 tree[i].min_val=inf;
    30 if(l==r)
    31 return;
    32 int mid=(l+r)/2;
    33 build(2*i,l,mid);
    34 build(2*i+1,mid+1,r);
    35 }
    36
    37 void updata(int i,int w) //更新区间[l,r]
    38 {
    39 if(tree[i].l>w || tree[i].r<w)
    40 return;
    41 if(tree[i].l>=w && tree[i].r<=w)
    42 {
    43 tree[i].min_val=w;
    44 return;
    45 }
    46 updata(2*i,w);
    47 updata(2*i+1,w);
    48 tree[i].min_val=min(tree[2*i].min_val,tree[2*i+1].min_val);
    49 }
    50
    51 int find(int i,int l,int r) //返回区间[l,r]的最小值
    52 {
    53 if(tree[i].l>r || tree[i].r<l)
    54 return inf;
    55 if(tree[i].l>=l && tree[i].r<=r)
    56 return tree[i].min_val;
    57 if(tree[i].l<tree[i].r)
    58 {
    59 return min(find(2*i,l,r),find(2*i+1,l,r));
    60 }
    61 return inf;
    62 }
    63
    64 int fun(int y) //直接查找
    65 {
    66 int i,ans=inf,k;
    67 for(i=len;i>=1;i--)
    68 {
    69 if(val[i]%y==0)
    70 {
    71 return i;
    72 }
    73 if(val[i]%y<ans)
    74 {
    75 ans=val[i]%y;
    76 k=i;
    77 }
    78 }
    79 return k;
    80 }
    81
    82 int solve(int mod)
    83 {
    84 int l=0,r=mod-1,ans=inf,k,temp;//[l,r]便是鸽笼的大小
    85 while(l<=500000)
    86 {
    87 if(r>500000)
    88 r=500000;
    89 temp=find(1,l,r);
    90 if(temp!=inf && temp%mod<ans)
    91 {
    92 ans=temp%mod;
    93 k=pos[temp];
    94 }
    95 else if(temp%mod==ans && pos[temp]>k)
    96 {
    97 k=pos[temp];
    98 }
    99 l+=mod;
    100 r+=mod;
    101 }
    102 return k;
    103 }
    104
    105 int main()
    106 {
    107 char c;
    108 int x,cas=1,i,ans;
    109 //freopen("d:\\in.txt","r",stdin);
    110 while(scanf("%d",&n)!=EOF && n)
    111 {
    112 if(cas>1)
    113 printf("\n");
    114 build(1,0,500000);
    115 printf("Case %d:\n",cas++);
    116 len=0;
    117 for(i=0;i<n;i++)
    118 {
    119 getchar();
    120 scanf("%c %d",&c,&x);
    121 if(c=='B')
    122 {
    123 len++;
    124 pos[x]=len;
    125 val[len]=x;
    126 updata(1,x);
    127 }
    128 else
    129 {
    130 if(len==0)
    131 {
    132 printf("-1\n");
    133 continue;
    134 }
    135 if(x<=5000)//范围较小的时候直接查找
    136 {
    137 ans=fun(x);
    138 printf("%d\n",ans);
    139 }
    140 else
    141 {
    142 ans=solve(x);
    143 printf("%d\n",ans);
    144 }
    145 }
    146 }
    147 }
    148 return 0;
    149 }



  • 相关阅读:
    shell 基本系统命令,关机重启,查看版本,查手册,日期,磁盘,历史命令
    shell 命令 文件查看ls,复制cp,移动mv,查看文件内容cat more less,查看文件信息 file
    luoguP1850 换教室
    bzoj2091: [Poi2010]The Minima Game DP
    luoguP1281 书的复制 DP,贪心
    loj6068. 「2017 山东一轮集训 Day4」棋盘 二分图,网络流
    bzoj1133: [POI2009]Kon
    luogu3426 [POI2005]SZA-Template 后缀树
    loj#2483. 「CEOI2017」Building Bridges 斜率优化 cdq分治
    loj2353. 「NOI2007」 货币兑换
  • 原文地址:https://www.cnblogs.com/ka200812/p/2248853.html
Copyright © 2020-2023  润新知