• [cf1349E]Slime and Hats


    首先,当发现全场不存在黑色帽子时,显然所有人都知道其是白色帽子,即必然离开

    当第一轮时,若第$n$个人发现前面$n-1$个人全是白色时,其自己必然是黑色,必然离开

    而第二轮时,若第$n-1$个人发现$n$没有离开,且前面$n-2$个人都是白色时,其自己必然是黑色(否则第$n$个人必然会在第一轮离开),其必然离开

    而第三轮时,若第$n-2$个人发现$n$和$n-1$都没有离开,且前面$n-3$个人都是白色时,其自己必然是黑色(否则若第$n-1$个人必然会离开),其必然离开

    以此类推,第一个离开的人即第一个黑色的人,假设其编号为$i$,其离开时间即为$n-i$

    同时,在$i$之前的人也都知道其为白色,其都会离开

    接下来的信息实际上是独立的,即无法利用到前面的信息,换言之会不断重复该过程

    (具体的可以类似分析,这里就不再描述了)

    例如,当$n=6$且$n$个人颜色依次为0 0 1 0 1 0,离开时间即依次为5 5 4 7 6 7

    为了方便,我们需要用更严谨的语言来描述此过程——

    令$c_{i}$为第$i$个数的颜色(0或1),$t_{i}$为第$i$个人离开的时间,则有
    $$
    egin{cases}t_{i}=sum_{1le jle i,c_{j}=1}n-j+1 & (c_{i}=1)\t_{i}=t_{nex_{i}}+1&(c_{i}=0)end{cases}
    $$
    (其中$nex_{i}$定义为$min_{ile jle n,c_{j}=1}j$,若不存在$j$则定义为$n+1$,且约定$c_{n+1}=1$)

    由此,对于$i<j$,若$t_{i}$和$t_{j}$已经确定,根据其关系不难确定一些$c_{i}$,即:

    1.若$t_{i}ge t_{j}+2$,无解(保证有解,即必然不存在此类情况)

    2.若$t_{i}=t_{j}+1$,则$c_{j}=1$且$forall ile k<j,c_{k}=0$

    3.若$t_{i}=t_{j}$,则$forall ile kle j,c_{k}=0$或$exists i<k<j,c_{k}=1$且$sum_{k=i}^{n}c_{k}=1$

    4.若$t_{i}<t_{j}$,则上述情况都不满足

    (注意$t_{n+1}$一定未被确定,因此上述结论成立)

    由此,我们将所有非0的$t_{i}$取出,将其划分为若干段单调不上升的连续子序列(假设共$m$段),记其中第$i$段以$t_{l_{i}}$为开头,以$t_{r_{i}}$为结尾,且定义两个参数$R_{i}=nex_{l_{i}}$和$T_{i}=t_{R_{i}}$

    当我们确定$R_{i-1}$和$T_{i-1}$后,来考虑$R_{i}$和$T_{i}$(特别的,$R_{0}=T_{0}=0$),分类讨论:

    Case 1:$l_{i}<r_{i}$,此时必然有$T_{i}=c_{l_{i}}-1$,继续分类讨论——

    Case 1.1:$t_{l_{i}}=t_{r_{i}}+1$,此时根据结论2,有$c_{l_{i}}=c_{l_{i}+1}=...=c_{r_{i}-1}=0$且$c_{r_{i}}=1$,那么不难得到$R_{i}=r_{i}$,此时来构造前面$(R_{i-1},l_{i})$之间1的情况

    这些1的目的是保证$T_{i}$的值正确,更具体的,要在$(n-l_{i}+1,n-R_{i-1}]$中选择若干个不同的数(构造),使得其和为$T_{i}-T_{i-1}-(n-R_{i}+1)$(以下该值记作$Delta_{i}$)

    (关于这一类问题,为了不影响分类讨论,在分类讨论结束后再考虑)

    Case 1.2:$t_{l_{i}}=t_{r_{i}}$,此时根据结论3,有两种情况,但注意到当第2种情况时,会有$forall l_{i}le kle n,t_{k}le t_{l_{i}}$,即若是此类情况其必要条件为$i=m$,那么继续分类讨论——

    Case 1.2.1:$i<m$,此时根据结论3,有$c_{l_{i}}=c_{l_{i}+1}=...=c_{r_{i}}=0$(因为若是第2种情况,则显然$l_{i}$之后所有数都应该小于等于$t_{l_{i}}$),此时$R_{i}$即在$(r_{i},l_{i+1})$中任选

    但当选出$R_{i}$后,我们需要保证可以在$(n-l_{i}+1,n-R_{i-1}]$中选择若干个不同的数,和为$Delta_{i}$,且对于所有可行的$R_{i}$,可以贪心取其中最小的(即上面的范围扩大)

    Case 1.2.2:$i=m$,此时不论$R_{i}$是在$(l_{i},r_{i})$中还是$(r_{i},n+1]$中,都可以令$forall R_{i}<jle n,c_{j}=0$(事实上这后面可以任意填,但这里就强制为0了)

    $R_{i}$的选择范围是$[l_{i},n+1]$且$t_{R_{i}} e 0$,此时在其中找到任意一个合法(可以在$(n-l_{i}+1,n-R_{i}]$选择若干个数和为$Delta_{i}$)即可

    Case 2:$l_{i}=r_{i}$,此时$T_{i}=c_{l_{i}}$或$c_{l_{i}-1}$,且两种情况不像$R_{i}$在多种选择中可以贪心取最小,因此我们使用dp来解决,即用$f_{i,j}$表示$T_{i}=c_{l_{i}}-j$时最小的$R_{i}$,两类转移分类讨论——

    Case 2.1:$f_{i,0}$的转移即与Case 1.1相同

    Case 2.2:$f_{i,1}$的转移即与Case 1.2相同

    (特别的,用$f_{i,j}=-1$表示该类不存在可行的$R_{i}$,那么对于**Case 1**中的情况只需要强制其为-1也可以以此法进行dp)

    还需要考虑以下三个未解决的问题:

    1.用$[l,r]$中选若干个数和为$x$

    2.在$[l,r]$中选若干个数和在$[L,R]$中且尽量小

    3.在$[l,r]$中选若干个数和在$S$中($S$为一个集合)

    第3个问题由于仅有$i=m$时需要处理,因此可以$o(n)$暴力枚举$S$转换为第1个问题

    对于第1个问题,考虑$[l,r]$所能表示的数,虽然并不一定是一个连续区间,但当选择的数个数确定时,一定是一个连续区间

    更具体的,假设选择$t$个数($0le tle r-l+1$),所能表达的数为$[sum_{i=l}^{l+t-1}i,sum_{i=r-t+1}^{r}i]$,显然这个区间的左右端点具有单调性,即可以对$t$二分并判定是否包含

    (关于这个区间的正确性,总是存在一个数使得其可以加1来构造即可)

    当找到对应一个$t$后,若强制选$l$后,显然之后能覆盖的区间为$[sum_{i=l}^{l+t-1}i,l+sum_{i=r-t+2}^{r}i]$,类似地选$r$后能覆盖的区间为$[sum_{i=l}^{l+t-2}i+r,sum_{i=r-t+1}^{r}i]$,当$tge 2$时两者的并即为原区间

    换言之,只需要不断选择$l$或$r$(要判断区间是否仍然包含$x$,总存在一个区间包含$x$)直至$tle 1$即可

    这样的复杂度是$o(r-l)$,在$f_{i,j}$转移时并不需要具体构造出方案,只需要在确定后构造即可,由于$r-l$即所有空隙长度和,总复杂度为$o(n)$

    对于第2个问题,只是在转移时求,因此并不需要构造出方案,同样去二分找到第一个与$[L,R]$有公共点的$t$即可处理

    综上,总复杂度为$o(nlog n)$,可以通过

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 200005
      4 #define ll long long
      5 vector<int>v;
      6 int n,m,l[N],r[N],R[N],ans[N],f[N][2],fr[N][2];
      7 ll t[N],T[N];
      8 ll sum(int l,int r){
      9     return 1LL*(l+r)*(r-l+1)/2;
     10 }
     11 ll find(int l,int r,ll L,ll R){
     12     if (L>R)return -1;
     13     int x=0,y=r-l+1;
     14     while (x<y){
     15         int mid=(x+y>>1);
     16         if (sum(r-mid+1,r)>=L)y=mid;
     17         else x=mid+1;
     18     }
     19     if ((sum(r-x+1,r)<L)||(R<sum(l,l+x-1)))return -1;
     20     return max(sum(l,l+x-1),L);
     21 }
     22 void calc(int l,int r,ll k){
     23     int x=0,y=r-l+1;
     24     while (x<y){
     25         int mid=(x+y>>1);
     26         if (sum(r-mid+1,r)>=k)y=mid;
     27         else x=mid+1;
     28     }
     29     if ((sum(r-x+1,r)<k)||(k<sum(l,l+x-1)))assert(0);
     30     while (x>1){
     31         if (k<=l+sum(r-x+2,r)){
     32             k-=l;
     33             ans[n-l+1]=1;
     34             l++;
     35         }
     36         else{
     37             k-=r;
     38             ans[n-r+1]=1;
     39             r--;
     40         }
     41         x--;
     42     }
     43     if (x==1)ans[n-k+1]=1;
     44 }
     45 int main(){
     46     scanf("%d",&n);
     47     for(int i=1;i<=n;i++)scanf("%lld",&t[i]);
     48     for(int i=1;i<=n;i++)
     49         if (t[i]>0)v.push_back(i);
     50     for(int i=0;i<v.size();i++)
     51         if ((!i)||(t[v[i]]>t[v[i-1]])){
     52             if (m)r[m]=v[i-1];
     53             l[++m]=v[i];
     54         }
     55     if (v.size())r[m]=v.back();
     56     l[m+1]=n+1;
     57     f[0][1]=-1;
     58     for(int i=1;i<=m;i++)
     59         for(int j=0;j<2;j++){
     60             f[i][j]=-1;
     61             if ((l[i]<r[i])&&(!j))continue;
     62             ans[0]=ans[1]=-1;
     63             for(int jj=0;jj<2;jj++){
     64                 if (f[i-1][jj]<0)continue;
     65                 ll delta=(t[l[i]]-j)-(t[l[i-1]]-jj);
     66                 if ((t[l[i]]>t[r[i]])||(l[i]==r[i])&&(!j)){
     67                     if (find(n-l[i]+2,n-f[i-1][jj],delta-(n-r[i]+1),delta-(n-r[i]+1))>=0)ans[jj]=r[i];
     68                 }
     69                 else{
     70                     if (i==m){
     71                         ans[jj]=-1;
     72                         for(int k=l[i];k<=n+1;k++)
     73                             if ((!t[k])&&(find(n-l[i]+2,n-f[i-1][jj],delta-(n-k+1),delta-(n-k+1))>=0)){
     74                                 ans[jj]=k;
     75                                 break;
     76                             }
     77                         continue;
     78                     }
     79                     ll s=find(n-l[i]+2,n-f[i-1][jj],delta-(n-r[i]),delta-(n-l[i+1]+2));
     80                     if (s>=0)ans[jj]=s-(delta-(n+1));
     81                 }
     82             }
     83             if ((ans[0]<0)&&(ans[1]<0))continue;
     84             if ((ans[0]>=0)&&((ans[1]<0)||(ans[0]<ans[1])))fr[i][j]=0;
     85             else fr[i][j]=1;
     86             f[i][j]=ans[fr[i][j]];
     87         }
     88     int lst=-1;
     89     if (f[m][0]>=0)lst=0;
     90     if (f[m][1]>=0)lst=1;
     91     memset(ans,0,sizeof(ans));
     92     for(int i=m;i;i--){
     93         R[i]=f[i][lst];
     94         T[i]=t[l[i]]-lst;
     95         lst=fr[i][lst];
     96         if (R[i]<=n)ans[R[i]]=1;
     97     }
     98     for(int i=1;i<=m;i++)calc(n-l[i]+2,n-R[i-1],T[i]-T[i-1]-(n-R[i]+1));
     99     for(int i=1;i<=n;i++)printf("%d",ans[i]);
    100 }
    View Code
  • 相关阅读:
    简单的StringBuffer实现
    Java-HashMap、HashSet、hashTable
    JavaScript 引用错误
    使用jconsole分析内存情况-JVM
    Thread 与 Runnable 混合使用测试
    裴波那序列-JAVA实现
    多线程之----------线程池
    winform 控件拖拽和缩放
    C# 使用Process调用外部程序中所遇到的参数问题
    winform textbox 的自动实现功能
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14636738.html
Copyright © 2020-2023  润新知