zoukankan      html  css  js  c++  java
  • 模拟106 题解

    A. 合并集合

    显然的区间dp。

    断环成链,预处理出每个连续区间集合的元素个数。

    然后直接dp就完了。

    B. climb

    想了一些简单的贪心,然后都伪了。

    所以考虑如何暴力$O(n^2)$来做这个题。

    枚举最终用来跳最后一步的药丸,显然前面的药丸可以按$a_i-b_i$排序。

    然后考虑如何优化这个过程,问题在于如何判断溺水的情况。

    溺水的情况只出现在$a_i-b_i$的前缀和小于等于$c_i$的前缀和。

    然而对于单个元素的删除添加,很多元素的下标会出现移位,然后就很难搞。实际上并没有很多,是我弱智了

    一个方法是线段树分治,首先对所有的药丸按$a_i-b_i$从大到小排序。

    $solve(l,r)$表示用来跳最后一步的药丸在$(l,r)$范围内,已经考虑其它状态。

    这个做法的好处在于,当$(l,r)$确定,除掉这个区间内每个药丸的排名也是确定的。

    维护两棵线段树,下标为排名,

    其中第一棵维护$a_i-b_i-c_i$前缀和的最小值,第二棵维护$a_i-b_i$前缀和的最大值。

    递归$solve(mid+1,r)$之前,将$[l,mid]$之间的贡献统计,暴力求出范围内的前缀和,之后区间加$l-1$前缀和的值。

    同理递归$solve(l,mid)$之前,将$[mid+1,r]$之间的贡献统计,暴力求出范围内的前缀和,更新$[r+1,n]$之间元素的值。

    当递归到$l=r$,可以现在第一棵线段树上二分确定不会溺水的边界,再在第二棵线段树上二分确定最早可以跳出的时间,更新最终答案就好了。

    虽然分治套线段树,但是这个做法的复杂度是$O(nlogn)$的。

    因为$solve$函数显然只会调用$n$次,对于每次调用:

    在线段树上确定区间是$log$复杂度的,因为线段树集合划分的性质,之后暴力递归到叶子节点是与长度线性相关的。

    对于最终$l=r$,两次二分都在线段树上实现,所以复杂度也是正确的。

    考场上打了$4K$代码,一遍过对拍。

    起初有两处导致复杂度$O(nlog^2)$的地方,最终一步步改到了$O(nlogn)$,打起来确实很爽。

    然而实际上我的做法并没有什么必要,因为邻项的修改只会更改一位上的值,普通线段树也可以实现。

      1 #include<algorithm>
      2 #include<iostream>
      3 #include<cstdio>
      4 #define ll long long
      5 #define cri const register 
      6 #define lch p<<1
      7 #define rch p<<1|1
      8 using namespace std;
      9 const int N=1e5+7;
     10 const int inf=0x3f3f3f3f;
     11 struct YaoWan{
     12     int a,b;
     13 }med[N];
     14 int n,ans=inf;
     15 long long lim,pre[N],w[N];
     16 struct node{
     17     ll val[2],lzy[2];//0表示最小值 减pre之后的 
     18 }s[N<<2];//1表示最大值 不减pre的
     19 inline void down(cri int p){
     20     if(s[p].lzy[0]){
     21         s[lch].val[0]+=s[p].lzy[0];
     22         s[rch].val[0]+=s[p].lzy[0];
     23         s[lch].lzy[0]+=s[p].lzy[0];
     24         s[rch].lzy[0]+=s[p].lzy[0];
     25         s[p].lzy[0]=0;
     26     }
     27     if(s[p].lzy[1]){
     28         s[lch].val[1]+=s[p].lzy[1];
     29         s[rch].val[1]+=s[p].lzy[1];
     30         s[lch].lzy[1]+=s[p].lzy[1];
     31         s[rch].lzy[1]+=s[p].lzy[1];
     32         s[p].lzy[1]=0;
     33     }
     34 }
     35 inline void up(cri int p){
     36     s[p].val[0]=min(s[lch].val[0],s[rch].val[0]);
     37     s[p].val[1]=max(s[lch].val[1],s[rch].val[1]);
     38 }
     39 void build(cri int p,cri int l,cri int r){
     40     if(l==r) return s[p].val[0]=-pre[l],void();
     41     int mid=l+r>>1;
     42     build(lch,l,mid);
     43     build(rch,mid+1,r);
     44     up(p);
     45 }
     46 void toadd(cri int p,cri int fl,cri int l,cri int r,cri int L,cri int R,cri int f){
     47     if(L==R) return s[p].val[0]+=w[L-l+fl]*f,s[p].val[1]+=w[L-l+fl]*f,void();
     48     down(p);
     49     int mid=L+R>>1;
     50     if(l<=mid) toadd(lch,fl,l,r,L,mid,f);
     51     if(r>mid) toadd(rch,fl,l,r,mid+1,R,f);
     52     up(p);
     53 }
     54 void modify(cri int p,cri int l,cri int r,cri ll val,cri int L,cri int R){
     55     if(L>=l&&R<=r) return s[p].lzy[0]+=val,s[p].lzy[1]+=val,s[p].val[0]+=val,s[p].val[1]+=val,void();
     56     down(p);
     57     int mid=L+R>>1;
     58     if(l<=mid) modify(lch,l,r,val,L,mid);
     59     if(r>mid) modify(rch,l,r,val,mid+1,R);
     60     up(p);
     61 }
     62 ll query(cri int p,cri int l,cri int r,cri int L,cri int R){
     63     if(L>=l&&R<=r) return s[p].val[1];
     64     down(p);
     65     int mid=L+R>>1;
     66     if(r<=mid) return query(lch,l,r,L,mid);
     67     if(l>mid) return query(rch,l,r,mid+1,R);
     68     return max(query(lch,l,r,L,mid),query(rch,l,r,mid+1,R));
     69 }
     70 int ask(cri int p,cri ll add,cri int L,cri int R){
     71     if(L==R) return L;
     72     down(p);
     73     int mid=L+R>>1;
     74     if(s[lch].val[1]+add>=lim) return ask(lch,add,L,mid);
     75     else return ask(rch,add,mid+1,R);
     76 }
     77 int find(cri int p,cri int L,cri int R){
     78     if(L==R) return L;
     79     down(p);
     80     int mid=L+R>>1;
     81     if(s[lch].val[0]<=0) return find(lch,L,mid);
     82     else return find(rch,mid+1,R);
     83 }
     84 void solve(cri int l,cri int r){
     85     ll val;
     86     if(l==r){
     87         if(l!=1&&l!=n){
     88             val=query(1,l-1,l-1,1,n-1);
     89             modify(1,l,n-1,val,1,n-1);
     90         }
     91         register int fr=n-1;
     92         if(s[1].val[0]<=0) fr=find(1,1,n-1)-1;
     93         if(fr&&query(1,1,fr,1,n-1)+med[l].a>=lim) ans=min(ans,ask(1,med[l].a,1,n-1)+1);
     94         if(l!=1&&l!=n) modify(1,l,n-1,-val,1,n-1);
     95         return ;
     96     }
     97     cri int mid=l+r>>1;
     98     w[mid]=0;
     99     for(register int i=mid+1;i<=r;++i) w[i]=w[i-1]+med[i].a-med[i].b;
    100     toadd(1,mid+1,mid,r-1,1,n-1,1);
    101     if(r!=n) modify(1,r,n-1,w[r],1,n-1);
    102     solve(l,mid);
    103     w[mid]=0;
    104     for(register int i=mid+1;i<=r;++i) w[i]=w[i-1]+med[i].a-med[i].b;
    105     toadd(1,mid+1,mid,r-1,1,n-1,-1);
    106     if(r!=n) modify(1,r,n-1,-w[r],1,n-1);
    107     
    108     w[l-1]=0;
    109     for(register int i=l;i<=mid;++i) w[i]=w[i-1]+med[i].a-med[i].b;
    110     toadd(1,l,l,mid,1,n-1,1);
    111     if(l!=1){
    112         val=query(1,l-1,l-1,1,n-1);
    113         modify(1,l,mid,val,1,n-1);
    114     }
    115     solve(mid+1,r);
    116     w[l-1]=0;
    117     for(register int i=l;i<=mid;++i) w[i]=w[i-1]+med[i].a-med[i].b;
    118     toadd(1,l,l,mid,1,n-1,-1);
    119     if(l!=1) modify(1,l,mid,-val,1,n-1);
    120 }
    121 inline bool cmp(const YaoWan &x,const YaoWan &y){
    122     return x.a-x.b>y.a-y.b;
    123 }
    124 inline int read(register int x=0,register char ch=getchar()){
    125     for(;!isdigit(ch);ch=getchar()) ;
    126     for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
    127     return x;
    128 }
    129 int main(){
    130     freopen("climb.in","r",stdin);
    131     freopen("climb.out","w",stdout);
    132     n=read(); scanf("%lld",&lim);
    133     for(int i=1;i<=n;++i){
    134         med[i].a=read(); med[i].b=read();
    135         if(med[i].a>=lim) return printf("%d
    ",1),0;
    136     }
    137     for(int i=1;i<=n;++i) pre[i]=pre[i-1]+read();
    138     if(n==1) return puts("-1"),0;
    139     sort(med+1,med+n+1,cmp);
    140     build(1,1,n-1);
    141     solve(1,n);
    142     printf("%d
    ",ans==inf?-1:ans);
    143     return 0;
    144 }
    T2

    C. coin

    当存在一种方案,使得二者共同努力可以达到全部硬币正面朝上,答案显然不会低于2。

    否则答案只与行数+列数的奇偶性有关。

    所以首先判局面是否可以转化到全部硬币正面朝上:

    对于每一个硬币,

    如果它是正面,那么行翻转对应列翻转,行不翻转对应列不翻转。

    如果它是反面,那么行翻转对应列不翻转,行不翻转对应列翻转。

    发现这个东西可以用拓展域并查集简单维护,

    如果最终态存在矛盾,那么无解,否则存在至少一组解。

    之后考虑先手是否必胜。

    对于每一个集合,如果该集合对应着偶数个翻转,其对立集合对应着偶数个翻转,那么显然可以忽略这个集合。

    其余的两个情况是:

    该集合对应着奇数个翻转,其对立集合对应着偶数个翻转。

    该集合对应着奇数个翻转,其对立集合对应着奇数个翻转。

    所以只要考虑后两种情况,设$dp(i,j)$表示前一种状态有$i$个,后一种状态有$j$个。

    显然$dp(0,0)=0$,对应先手必败。

    有转移

    $dp(i,j)|=!dp(i,j-1)$,表示简单选择一个两面都是奇数的集合,将其转化为没用的偶数集合。

    $dp(i,j)|=!dp(i-1,j)$,表示选择一个奇偶面集合的奇面,将其转化为没用的偶数集合。

    $dp(i,j)|=!dp(i-1,j+1)$,表示选择一个奇偶面集合的偶面,那么多一个奇数集合。

  • 相关阅读:
    python3.5过滤网址和图片的函数自己亲测可用
    关于接口数据编写的思路整理
    Mysql db
    在Maven中设置Nexus私有服务为中央工厂
    maven 镜像使用
    maven 自建库
    Windows批处理(cmd/bat)常用命令小结
    springmvc and maven
    spring 源码解析
    spring aop 原理
  • 原文地址:https://www.cnblogs.com/skyh/p/11824295.html
Copyright © 2011-2022 走看看