• 【2018.11.26】玄机 / 画心 / 求索


    以后就是 $NOI$ 模拟赛了!!!

    题目

    WZJ题解

    T1

    一开考就口胡了这道题……

    首先遇到这种总字符数特别少的多模板串匹配,肯定要建一个 $AC$ 自动机。

    然后想到了维护矩阵,同时记录 转移到的点 和 匹配过的点的集合。

    但是这样的复杂度是 $O((50 imes m)^3 imes log(n))$,会被卡。

    所以不能维护 匹配过的点的集合,把它抽到外面去,这样能省很多矩乘复杂度。

    不知道你还记不记得这么一道题:GT考试

    那一题是不让你匹配出不吉利数字串。

    然后回来看看这题,发现两者求的东西其实是相反的。

    所以这题可以枚举不出现哪些串,在预处理转移矩阵时,不让根节点能够转移到一个点,使得根节点到这个点的路径组成的串的后缀为一个不能出现的串。

    由于转移矩阵经过一次转移,最多多匹配一位,也就是匹配到它的儿子,所以对于一个后缀为一个不能出现的串的串在 $AC$ 自动机上的底部节点(可能不存在,那就不用管),当匹配到这个点的父亲时,判断一下,不让它匹配到这个点就行了。这样就没法转移到这个点及其下面的所有点了。

    最后套一个容斥原理就行了:不出现任意一个串的情况数 = 不出现 $1$ 个串的情况数 - 不出现 $2$ 个串的情况数 + 不出现 $3$ 个串的情况数 - 不出现 $4$ 个串的情况数。

    答案就是 总情况数 - 不出现任意一个串的情况数。

    时间复杂度 $O(2^m imes 50^3 imes log(n))$。

    另外一定要搞清矩阵的横乘和纵乘是不一样的!!横乘是 $a(i,k) imes b(k,j)$,纵乘是 $b(i,k) imes a(k,j)$!!现场用垃圾纵乘然后写反,成功表演爆 $0$……

      1 #include<bits/stdc++.h>
      2 #define ll long long
      3 #define mod 998244353
      4 using namespace std;
      5 inline int read(){
      6     int x=0; bool f=1; char c=getchar();
      7     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
      8     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
      9     if(f) return x;
     10     return 0-x;
     11 }
     12 int m,mm,n,tag[55];
     13 ll f[55][55],ans;
     14 char c[5][55];
     15 
     16 int que[55],nxt[55];
     17 namespace AC{
     18     int tr[55][26],Tr[55][26],mark[55],cnt;
     19     void ins(char ch[55],int id){
     20         int len=strlen(ch),i,u=0,v;
     21         for(i=0;i^len;++i){
     22             v=ch[i]^'0';
     23             if(!tr[u][v]) tr[u][v]=Tr[u][v]=++cnt;
     24             u=tr[u][v];
     25         }
     26         mark[u]|=(1<<id);
     27     }
     28     void getFail(){
     29         int i,u,hd=1,tl=0;
     30         for(int i=0;i^10;++i) if(tr[0][i]) que[++tl]=tr[0][i];
     31         while(hd<=tl){
     32             u=que[hd++], mark[u]|=mark[nxt[u]];
     33             for(i=0;i^10;++i){
     34                 if(!tr[u][i]) {tr[u][i]=tr[nxt[u]][i];}
     35                 else{
     36                     que[++tl]=tr[u][i];
     37                     nxt[tr[u][i]]=tr[nxt[u]][i];
     38                 }
     39             }
     40         }
     41     }
     42 };
     43 using namespace AC;
     44 void mul(ll a[55][55], ll b[55][55]){
     45     int i,j,k; ll tmp[55][55];
     46    /* cout<<"++"<<endl;
     47     for(i=0;i<=cnt;++i){
     48             for(j=0;j<=cnt;++j)
     49                 printf("%d ",b[i][j]);
     50             putchar('
    ');
     51         }*/
     52     //cout<<"++"<<endl;
     53     for(i=0;i<=cnt;++i)
     54         for(j=0;j<=cnt;++j){
     55             tmp[i][j]=0;
     56             for(k=0;k<=cnt;++k)
     57                 (tmp[i][j]+=a[k][j]*b[i][k])%=mod;
     58         }
     59     for(i=0;i<=cnt;++i)
     60         for(j=0;j<=cnt;++j)
     61             a[i][j]=tmp[i][j];
     62 }
     63 ll a[55][55],b[55][55];
     64 ll qpow(ll x,int y){
     65     ll ret=1;
     66     while(y>0){
     67         if(y&1) ret=(ret*x)%mod;
     68         x=(x*x)%mod;
     69         y>>=1;
     70     }
     71     return ret;
     72 }
     73 ll qpow(int x){
     74     int i,j;
     75     memset(a,0,sizeof(a));
     76     a[0][0]=1;
     77     while(x>0){
     78         if(x&1) mul(a,b);
     79         mul(b,b);
     80         x>>=1;
     81     }
     82     ll res=0;
     83     for(i=0;i<=cnt;++i) res=(res+a[i][0])%mod;
     84     return res;
     85 }
     86 void dfs(int u,int x,int mx){
     87     if(u){
     88         memset(b,0,sizeof b);
     89         int i,j,t;
     90         que[1]=0;
     91         /*
     92         for(int hd=1,tl=1;hd<=tl;++hd){
     93             i=que[hd];
     94             for(j=0;j^m;++j)
     95                 if(u&(1<<j) && tag[j]==i){ed[u]=1; break;}
     96             for(j=0;j^10;++j)
     97                 if(Tr[i][j]) que[++tl]=Tr[i][j], ed[Tr[i][j]]=ed[i];
     98         }*/
     99        // for(int j=0;j<=cnt;++j)if(mark[j]&u)ed[j]=1;
    100         //cout<<"-------"<<endl;
    101         for(i=0;i<=cnt;++i){
    102             for(j=0;j^10;++j){
    103                 t=tr[i][j];
    104                 //printf("back:%d %d %d
    ",j,t,i);
    105                 if(!(mark[t]&u)) b[t][i]=(b[t][i]+1)%mod;
    106             }
    107         }
    108         /*cout<<"-------"<<endl;
    109         for(i=0;i<=cnt;++i){
    110             for(j=0;j<=cnt;++j)
    111                 printf("%d ",b[i][j]);
    112             putchar('
    ');
    113         }*/
    114         ans+=qpow(n)*x;
    115       //  printf("Hey buddy:%d %d %lld %lld
    ",u,x,haha,ans);
    116         ans=(ans%mod+mod)%mod;
    117     }
    118     if(!(u&1) && (u|1)<mm && 1>mx) dfs(u|1,-x,max(mx,1));
    119     if(!(u&2) && (u|2)<mm && 2>mx) dfs(u|2,-x,max(mx,2));
    120     if(!(u&4) && (u|4)<mm && 4>mx) dfs(u|4,-x,max(mx,4));
    121     if(!(u&8) && (u|8)<mm && 8>mx) dfs(u|8,-x,max(mx,8));
    122 }
    123 int main(){
    124     //freopen("password.in","r",stdin);
    125     //freopen("password.out","w",stdout);
    126     m=read(),n=read();
    127     mm=1<<m;
    128     int i,j;
    129     for(i=0;i^m;++i){
    130         scanf("%s",c[i]);
    131         ins(c[i],i);
    132     }
    133     getFail();
    134     dfs(0,-1,0);
    135     printf("%lld
    ",((qpow(10,n)-ans)%mod+mod)%mod);
    136     return 0;
    137 }    
    View Code

    T2

    实(W)践(Z)证(J)明(shuo)线段树分治是基础操作

    我不会基础操作,我该退役了 sro

    容易发现 $40$ 分 $noip$ 难度的 $dp$ 是白送的。

    $60$ 分是个简单递推维护凸包的斜率优化(因为前缀和 $S_i$ 是单调递增的,可以把队头小于当前斜率的线段 依次删掉),不过我好像很少写这个……

    $80$ 分

     1 #include<bits/stdc++.h>
     2 #define N 152505
     3 #define ll long long
     4 #define ld long double
     5 const ll inf=(1ll<<63)-1; 
     6 using namespace std;
     7 inline int read(){
     8     int x=0; bool f=1; char c=getchar();
     9     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    10     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    11     if(f) return x;
    12     return 0-x;
    13 }
    14 int n,a,b,c,l,r,s[N];
    15 int hd,tl,q[N];
    16 ll f[N];
    17 inline ll gy(int j){return f[j]+(ll)a*s[j]*s[j];}
    18 inline ll getAns(int i,int j){return gy(j)-2ll*a*s[i]*s[j]+(ll)a*s[i]*s[i]+c;}
    19 inline void push(int i){
    20     while(hd<tl && ((ld)gy(i)-(ld)gy(q[tl])) * ((ld)s[q[tl]]-(ld)s[q[tl-1]]) > ((ld)gy(q[tl])-(ld)gy(q[tl-1])) * ((ld)s[i]-(ld)s[q[tl]])) --tl;
    21     q[++tl]=i;
    22 }
    23 inline bool jud(int i,int j){return (ld)gy(q[j+1])-(ld)gy(q[j]) <= (ld)2*(ld)a*(ld)s[i]*((ld)s[q[j+1]]-(ld)s[q[j]]);}
    24 ll getMx(int i){
    25     ll ans=getAns(i,q[tl]);
    26     int l=hd, r=tl-1, mid;
    27     while(l<=r){
    28         mid=(l+r)>>1;
    29         ans=max(ans,max(getAns(i,q[mid]),getAns(i,q[mid+1])));
    30         if(jud(i,mid)) r=mid-1;
    31         else l=mid+1;
    32     }
    33     //printf("upd:%d %lld
    ",i,ans);
    34     return ans;
    35 }
    36 int id[N],tmp[N];
    37 void cdq(int l,int r){ 
    38     if(l==r) return; 
    39     int mid=(l+r)>>1, i;
    40     cdq(l,mid);
    41     hd=1, tl=0, q[++tl]=id[l];
    42     for(i=l+1;i<=mid;++i) push(id[i]);
    43     for(;i<=r;++i) f[i]=max(f[i],getMx(i));
    44     cdq(mid+1,r);
    45     int now=0, j;
    46     i=l;
    47     for(j=mid+1;j<=r;++j){
    48         while(i<=mid && s[id[i]]<s[id[j]]) tmp[++now]=id[i++];
    49         tmp[++now]=id[j];
    50     }
    51     while(i<=mid) tmp[++now]=id[i++];
    52     for(i=l;i<=r;++i) id[i]=tmp[i-l+1];/*
    53     printf("cdq:%d %d
    ",l,r);
    54     for(i=l;i<=r;++i) printf("%d %d
    ",id[i],s[id[i]]);
    55     putchar('
    ');*/
    56 }
    57 int main(){
    58     //freopen("paint7.in","r",stdin);
    59     //freopen("paint.out","w",stdout);
    60     n=read(),a=read(),b=read(),c=read(),l=read(),r=read();
    61     int i;
    62     for(i=1;i<=n;++i) s[i]=s[i-1]+read(), id[i]=i;
    63     f[0]=0;
    64     for(i=1;i<=n;++i) f[i]=-inf;
    65     cdq(0,n);
    66     //for(i=1;i<=n;++i) printf("%lld
    ",f[i]);
    67     printf("%lld
    ",f[n]+(ll)s[n]*b);
    68     return 0;
    69 }
    View Code

    T3

    $5-6$ 组数据 就是把边权为 $0$ 的边全部删掉,然后在剩下的每个连通块中用两次 $dfs$ 找最远点求出直径,最后取所有直径的最大值。

    简单地说 $60$ 分以下是 $noip$ 难度。

    $80$ 分段开始就要套用各种奇怪的树上算法了。

    树上算法有几个?……数数好像没几个,而且这种题用点分治来暴力优化好像就可以了……

    设重心的儿子依次编号为 $1$ 到 $x$,每次在一部分树中找到重心(分治点)后,把它的所有出边都暴力跑一遍,动态记录所有从重心出发、经过编号为 $i$ 的儿子的所有路径的二维信息(路径边权的最小值 $Min_i$,路径边权之和 $sum_i$)。

    要合并两条路径为答案路径 $(x,y)$,设它们经过的儿子分别为 $a,b$,它们满足下列条件时

    $$a≠b$$

    $$Min_a>Minb$$

    它们合并的答案为 $Min_b imes (sum_a+sum_b)$。

    我们发现两个条件其实就是二维偏序,可以递推+数据结构($cdq$ 患者除外)统计答案,对于 $a≠b$,正反各跑一遍即可。

    所以依次遍历重心的儿子(其实编号顺序就是随意的,这个顺序也随意),当遍历到第 $i$ 个时,把 $sum_i$ 插入树状数组的第 $Min_a$ 位,然后把 $ans$ 累加上 $Min_b imes querySum(Min_b,inf)$(其中 $querySum(l,r)$ 表示查询树状数组 $[l,r)$ 区间的和,前缀和相减即可求出)。

    好,$100$ 分又是线段树分治,不会

  • 相关阅读:
    地址栏访问Action,后来方法执行两次
    转:Android中的Selector的用法
    转:android 自定义RadioButton样式
    Android中@id与@+id区别
    INSTALL_PARSE_FAILED_MANIFEST_MALFORMED 错误
    Supervisor
    mysql 赋予权限连接
    定时任务
    git 提交代码五部曲
    Mysql 之事物
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/2018_11_26.html
Copyright © 2020-2023  润新知