zoukankan      html  css  js  c++  java
  • 省选模拟8 题解

    A. 序列

    发现较难维护的是后一个信息。

    所以考虑直接分块维护。

    为每个块开一个vector,表示这个块中所有元素对vector中的所有元素依次取过max。

    因为取max的性质,这个vector中的元素只需要保留单调递增的部分。

    在查询的时候和暴力重构一些块的时候,可以直接在vector中二分确定某个元素更新了多少次。

    因为卡内存,所以每隔5000次询问暴力重构所有块一次。

    询问时手贱把upperbound打成lowerbound,哭了。

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 const int N=1e5+7;
     5 int n,m,sq;
     6 ll a[N],add[700];
     7 int sz[N],bl[N],tim[700];
     8 vector<ll> ve[700];
     9 inline void rebuild(int k){
    10     if(!tim[k]&&ve[k].empty()) return ; 
    11     int l=(k-1)*sq+1,r=min(n,k*sq);
    12     for(int i=l;i<=r;++i){
    13         sz[i]+=ve[k].end()-upper_bound(ve[k].begin(),ve[k].end(),a[i])+tim[k];
    14         if(!ve[k].empty()) a[i]=max(a[i],ve[k].back())+add[k];
    15         else a[i]+=add[k];
    16     }
    17     tim[k]=0; add[k]=0; ve[k].clear();
    18 }
    19 inline void modify(int l,int r,int c){
    20     if(!c) return ;
    21     rebuild(bl[l]); rebuild(bl[r]);
    22     for(int i=l,lim=min(bl[l]*sq,r);i<=lim;++i) ++sz[i],a[i]+=c;
    23     if(bl[l]!=bl[r]) for(int i=(bl[r]-1)*sq+1;i<=r;++i) ++sz[i],a[i]+=c;
    24     for(int i=bl[l]+1;i<bl[r];++i) add[i]+=c,++tim[i];
    25 }
    26 inline void insert(int l,int r,int c){
    27     rebuild(bl[l]); rebuild(bl[r]);
    28     for(int i=l,lim=min(bl[l]*sq,r);i<=lim;++i) if(c>a[i]) a[i]=c,++sz[i];
    29     if(bl[l]!=bl[r]) for(int i=(bl[r]-1)*sq+1;i<=r;++i) if(c>a[i]) a[i]=c,++sz[i];
    30     for(int i=bl[l]+1;i<bl[r];++i) if(ve[i].empty()||c-add[i]>ve[i].back()) ve[i].push_back(c-add[i]);
    31 }
    32 inline void query(int x){
    33     int k=bl[x];
    34     if(!ve[k].empty()) printf("%lld ",max(a[x],ve[k].back())+add[k]);
    35     else printf("%lld ",a[x]+add[k]);
    36     printf("%d
    ",sz[x]+ve[k].end()-upper_bound(ve[k].begin(),ve[k].end(),a[x])+tim[k]);
    37 }
    38 inline int read(register int x=0,register char ch=getchar(),register int f=0){
    39     for(;!isdigit(ch);ch=getchar()) f=ch=='-';
    40     for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
    41     return f?-x:x;
    42 }
    43 int main(){
    44     n=read(); sq=sqrt(n)*0.5+1;
    45     for(int i=1;i<=n;++i) a[i]=read(),bl[i]=(i-1)/sq+1;
    46     m=read();
    47     for(int i=1,l,r,c;i<=m;++i){
    48         char ch=getchar(); while(!isalpha(ch)) ch=getchar();
    49         if(ch=='A') l=read(),r=read(),c=read(),modify(l,r,c);
    50         else if(ch=='M') l=read(),r=read(),c=read(),insert(l,r,c);
    51         else query(read());
    52         if(!(i&4095)) for(int j=1;j<=bl[n];++j) rebuild(j);
    53     }
    54     return 0;
    55 }
    分块

    复杂度瓶颈在于修改时的暴力重构,复杂度为$O(n sqrt{nlogn})$。

    发现在重构时可以通过单调指针,所以维护每个块中元素的大小顺序,对于修改操作可以进行二路归并,总复杂度$O(nsqrt n)$。

    正解做法是离线后进行扫描线操作。

    将每个操作视为在左端点加入,右端点删除。

    容易发现可以把相邻的加法操作合在一起,于是操作序列转化为$A_1 M_1 A_2 M_2 A_3 M_3$的形式。

    考虑已经维护了原来的合法的取max操作,设$s_i$表示$A_i$的前缀和。

    取max操作能够成立仅当$M_{i-1}+s_i-s_{i-1}<M_i$。

    移项可得$M_{i-1}-s_{i-1}<M_i-s_{i}$,所以只要维护$M_i-s_i$形成的单调递增的结构就可以了。

    这个东西可以用线段树维护单调栈的思路维护。

    B. 旅行计划

    部分分很简单,但是正解很难想到。

    正解大概是取$d$为$k$和联通块内所有边权的gcd。

    答案显然只能为$d$的倍数,不妨将所有的边权和$k$都除掉$d$。

    存在一种情况,使得联通块内所有边权在模$k$意义下总和为$1$。

    然而只可以简单地构造一条回路,使得经过联通块内任意边任意偶数次(题解给出的构造方法是在DFS树上走,遇到非树边可以来回转)。

    所以构造出一条在模$k$意义下贡献为$2$的回路$C$是可行的。

    如果$k$为奇数,那么一定可以通过不断走$u ightarrow v ightarrow u$这条路径,使得最终答案为$0$。

    只需要考虑$k$为偶数。

    现在考虑$u v$之间是否存在一条长度为偶数(即在模$2$意义下为$0$)的路径。

    因为已经有了回路$C$,如果存在长度为偶数的路径,答案一定为$0$。

    如果不存在,那么答案只能为$1$。

    所以对于每个联通块分别奇偶染色(当然这里的染色标准并不是相邻异色),判是否为二分图就可以了。

    然而还有一个问题是,每次给出的$k$是不同的,所以没有办法预处理。

    然而注意到这里的$k$在除过$d$之后仍为偶数,也就是说$k$中质因子$2$的个数要比原联通块的$gcd$中$2$的个数要多。

    所以原联通块中边权在除两个不同的$gcd$之后的奇偶性是相同的,所以直接预处理是可行的。

    C. Hack

    可以发现直接用最小割就可以保证每条路径一定被割,但是却不能保证只割一次。

    手玩发现割不止一次的情况,等价于源点汇点分别形成的$S$/$T$集合中,满足$T$集合内存在边回到$S$集合,然后就死掉了。

    题解给出的做法是,对每条边建立边权为$inf$的反向边,这样上述的情况会存在增广路,所以上述情况并不是一个合法的割。

    所以直接跑最小割就可以了。

  • 相关阅读:
    Python 入门的一些练习题
    Parallel Data Augmentation for Formality Style Transfer 阅读
    Windows Internals 笔记——用户模式下的线程同步
    RSA算法原理(转)
    Ubuntu Git Server 搭建(Gitosis)
    Linux Make 报错:make: *** /lib/modules/3.10.0-1127.el7.x86_64/build: no such file or directory. stop.
    Centos 配置串口连接
    编写一个程序,将字符数组s2中的全部字符复制到字符数组s1中,不用strcpy函数。复制时,‘’也要赋值过去。''之后的字符不复制
    编写一个程序,将连个字符串s1和s2比较,如果s1 > s2,输出一个整数;若s1 = s2,输出0;若s1 < s2,输出一个负数。不要用strcpy函数。两个字符串用gets函数读入。输出的正数或负数的绝对值应是相比较的两个字符串相对应字符的ASCII码的差值。
    编一程序,将两个字符串连接起来,不要用strcat函数
  • 原文地址:https://www.cnblogs.com/skyh/p/12204056.html
Copyright © 2011-2022 走看看