• 高二一调(20190614)


    T1:Censoring

      和以前kmp一样的一道题,只是改成了多个串需要AC自动机

      用一个栈维护当前字符串,匹配上了就暴力弹栈,并将指针回溯,复杂度O(n+m)

      这题考试的时候不知道怎么把栈给否掉了,用了个玄学方法记录,只干出来13分

     

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define null NULL
    using namespace std;
    struct node{
        node *fi,*ch[26];
        int c;
        node (){
            fi=null;c=0;
            memset(ch,null,sizeof(ch));
        }
    }*root=new node();
    char a[2002][100002];int le[2002];
    short s[200];
    void insert(int x){
        node *now=root;int len=le[x];
        for(int i=1;i<=len;i++){
            int in=s[a[x][i]];
            if(now->ch[in]==null) now->ch[in]=new node();
            now=now->ch[in];
        }
        now->c=x;
    }
    void getfail(){
        queue<node*>q;
        for(int i=0;i<26;i++)
          if(root->ch[i]!=null){
              root->ch[i]->fi=root;
              q.push(root->ch[i]);
          }
          else root->ch[i]=root;
        while(!q.empty()){
            node *now=q.front();q.pop();
            for(int i=0;i<26;i++)
              if(now->ch[i]!=null){
                  now->ch[i]->fi=now->fi->ch[i];
                  q.push(now->ch[i]);
              }
              else now->ch[i]=now->fi->ch[i];
        }
    }
    node *last[100002];
    int top,sol[100002];char sta[100002];
    void query(){
        node *now=root;int len=le[0],del=0;
        for(int i=1;i<=len;i++){
            if(now==null) now=root;
            sta[++top]=a[0][i];last[top]=now;
            int out=s[a[0][i]];
            now=now->ch[out];
            if(now->c){
                top-=le[now->c];
                now=last[top+1];
            }
        }
    }
    int main(){
        for(int i='a';i<='z';i++) s[i]=i-'a';
        for(int i='A';i<='Z';i++) s[i]=i-'A';
        scanf("%s",a[0]+1);le[0]=strlen(a[0]+1);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",a[i]+1);
            le[i]=strlen(a[i]+1);
            insert(i);
        }
        getfail();
        query();
        for(int i=1;i<=top;i++)
          putchar(sta[i]);
        return 0;
    }
    AC代码

     

     

     

    T2:记忆的轮廓

      概率期望,考试时候直接弃了(主要是无良老师数据范围没给)

     

      50%:数据n==p,则不需要考虑存档,首先设d[i]表示i的儿子数。设g[i]表示对于一个错误节点i,期望走多少步会读档。那么

     

        g[i]=1+1/d[i]*∑{g[j]}其中j是i的儿子。

     

      接下来我们倒着递推:f[i]=1+1/d[i]*f[i+1]+1/d[i]*∑{g[j]+f[i]}[j是i的错误儿子]

     

      移项得:f[i]=d[i]+f[i+1]+Σg[j][j是i的错误儿子] 复杂度O(m)

     

      70%:我们设dp,f[i,j]表示当前存档点为i,还剩j次存档机会。

     

      首先我们需要预处理一个a[i,j],表示存档点为i,从i开始走到正确节点j的期望步数(中间不能存档)。

     

      显然有边界条件a[i,i]=0。对于i<j,可以列出递推式:

     

      a[i,j]=a[i,j-1]+1+1/d[j-1]*∑{g[k]+a[i,j]}[k是j-1的错误儿子]

     

      移项得a[i,j]=a[i,j-1]*d[j-1]+d[j-1]+Σg[k][k是j-1的错误儿子]

     

      则f[i][k]=min(f[j][k-1]+a[i][j]),答案f[1][p-1]

     

      复杂度O(pn^2)

     

    100%:优化方向1:我们观察a数组,发现有a[i,j]<a[i,j+1],a[i-1][j]>a[i][j]

     

             那么我们可以用单调队列优化,用两维的单调队列(当前和上一次)每次把所有f[i][k]插入

     

        每次查询的时候若队头que[st].dp+a[i][que[st].at]>=que[st+1].dp+a[i][que[st+1].at]||que[st].at<=i就出队

     

        然后理论上我们还需要二分来找到两个函数交点

     

        但是我没找它就过了......

     

        复杂度O(pn)

     

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define re register 
    using namespace std;
    struct node{
        int at;
        double dp;
    }que[2000][2];
    double g[2005],f[2005][705],d[2005],a[2005][2005];
    int to[2000][5],st[2],ed[2],now;
    void dfs(int u){
        int d=to[u][0];
        g[u]=1;
        for(int i=1;i<=d;i++){
            dfs(to[u][i]);
            g[u]+=g[to[u][i]]/d;
        }
    }
    int main(){
        int T,n,m,p,x,y;
        scanf("%d",&T);
        while(T--){
            memset(f,127,sizeof(f));memset(a,0,sizeof(a));memset(to,0,sizeof(to));
            scanf("%d%d%d",&n,&m,&p);p--;
            for(int i=1;i<=m-n;i++){
                scanf("%d%d",&x,&y);
                to[x][++to[x][0]]=y;
            }
            for(int i=1;i<=n-1;i++){
                d[i]=to[i][0];g[i]=0;
                for(int j=1;j<=d[i];j++){
                    dfs(to[i][j]);
                    g[i]+=g[to[i][j]];
                }
                d[i]+=1;
            }
            for(int i=1;i<=n-1;i++)
                for(int j=i+1;j<=n;j++)
                  a[i][j]=a[i][j-1]*d[j-1]+g[j-1]+d[j-1];
            st[now]=1;ed[now]=0;
            que[++ed[now]][now].dp=0;
            que[ed[now]][now].at=n;
            for(int k=1;k<=p;k++){
                now^=1;
                st[now]=1;ed[now]=0;
                
                for(int i=1;i<=n-1;i++){
                    while(st[now^1]<ed[now^1]&&(que[st[now^1]][now^1].at<=i||que[st[now^1]][now^1].dp+a[i][que[st[now^1]][now^1].at]>=que[st[now^1]+1][now^1].dp+a[i][que[st[now^1]+1][now^1].at])) st[now^1]++;
                    f[i][k]=que[st[now^1]][now^1].dp+a[i][que[st[now^1]][now^1].at];
                }
                for(int i=1;i<=n-1;i++) que[++ed[now]][now].dp=f[i][k],que[ed[now]][now].at=i;
                que[++ed[now]][now].dp=0;que[ed[now]][now].at=n;
            }
            printf("%.4lf
    ",f[1][p]);
        }
        return 0;
    }
    AC代码

     

     

     

      优化方向2:观察a数组,可以看到它是恐怖的增长的

     

        我们来估计答案的上界,考虑一种可行方案,每n/p个正确节点就设立一次存档位置,那么答案最大是多少呢?考虑最坏情况,观

     

      察a的转移,应该每变换一次存档点,

     

        设s[i]=Σg[j]大约需要3^(n/p)s[i]+3(n/p-1)*s[i+1]+3^(n/p-2)*s[i+2]+……因为最多m个节点,s的上限是1500(实际上也远远达不

     

        到)

     

      那么其实,针对答案不会特别大,a的增长又很恐怖,我们还可以思考对70%的算法优化。那就是设定一个常数step,每次转移最

     

      多从距当前step步远的位置转移过来。step取40多基本不会有问题了,因为a的下界已经是2^40了,而答案的上界远远没有 

     

         达到,经过精确计算还可以再把step调小一点。

     

      复杂度O(np log ans)

     

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define re register 
    using namespace std;
    struct node{
        int at;
        double dp;
    };
    deque<node>q;
    double g[2005],f[2005][705],d[2005],a[2005][2005];
    int to[2000][5];
    void dfs(int u){
        int d=to[u][0];
        g[u]=1;
        for(int i=1;i<=d;i++){
            dfs(to[u][i]);
            g[u]+=g[to[u][i]]/d;
        }
    }
    int main(){
        int T,n,m,p,x,y;
        scanf("%d",&T);
        while(T--){
            memset(f,127,sizeof(f));memset(a,0,sizeof(a));memset(to,0,sizeof(to));
            scanf("%d%d%d",&n,&m,&p);p--;
            for(int i=1;i<=m-n;i++){
                scanf("%d%d",&x,&y);
                to[x][++to[x][0]]=y;
            }
            for(int i=1;i<=n-1;i++){
                d[i]=to[i][0];g[i]=0;
                for(int j=1;j<=d[i];j++){
                    dfs(to[i][j]);
                    g[i]+=g[to[i][j]];
                }
                d[i]+=1;
            }
            for(int i=1;i<=n-1;i++)
                for(int j=i+1;j<=n;j++)
                  a[i][j]=a[i][j-1]*d[j-1]+g[j-1]+d[j-1];
            f[n][0]=0;
            for(int k=1;k<=p;k++)
              for(int i=n-1;i>=1;i--)
                for(int j=i+1;j<=n&&j-i<=40;j++)
                {
                    f[i][k]=min(f[i][k],f[j][k-1]+a[i][j]);
                    if(f[j+1][k-1]>f[j][k-1]) break;
                }
            printf("%.4lf
    ",f[1][p]);
        }
        return 0;
    }
    AC代码

     

     

     

    T3:动态开点+权值线段树合并+树上差分

     

      考试时候想到了没敢打,主要没分析好空间复杂度

     

      对每个点开权值线段树,离不离散都行

     

      复杂度时间,空间都是O(nlogn)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define re register 
    #define L ls[k]
    #define R rs[k]
    #define lc ls[k],l,mid
    #define rc rs[k],mid+1,r
    using namespace std;
    inline int read(){
        re int a=0;re char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+ch-'0',ch=getchar();
        return a;
    }
    int rt[100005],ans[100005],ls[5000000],rs[5000000],da[5000000],d[100005],f[100005][20],fr[100005],to[200005],la[200005],cnt,num,is[100005],tot;
    inline void add(int x,int y){
        to[++num]=y;
        la[num]=fr[x];
        fr[x]=num;
    }
    void dfs(int u,int fa){
        for(int i=1;(1<<i)<=d[u];i++) f[u][i]=f[f[u][i-1]][i-1];
        for(int i=fr[u];i;i=la[i])
          if(to[i]!=fa){
               d[to[i]]=d[u]+1;f[to[i]][0]=u;
               dfs(to[i],u);
          }
    }
    int getlca(int a,int b){
        if(d[a]<d[b]) swap(a,b);
        for(int i=18;i>=0;i--) 
            if(d[a]-(1<<i)>=d[b]) a=f[a][i];
        if(a==b) return a;
        for(int i=18;i>=0;i--)
            if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
        return f[a][0];
    }
    void insert(int &k,int l,int r,int x,int y){
        if(!k) k=++cnt;
        if(l==r){
            da[k]+=y;
            return;
        }
        re int mid=(l+r)>>1;
        if(x<=mid) insert(lc,x,y);
        else insert(rc,x,y);
        da[k]=max(da[L],da[R]); 
    }
    int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;
        if(l==r){
            da[x]+=da[y];
            return x;
        }
        re int mid=(l+r)>>1;
        ls[x]=merge(ls[x],ls[y],l,mid);
        rs[x]=merge(rs[x],rs[y],mid+1,r);
        da[x]=max(da[ls[x]],da[rs[x]]);
        return x;
    }
    int query(int k,int l,int r){
        if(!da[k]||!k) return 0;
        if(l==r) return l;
        re int mid=(l+r)>>1;
        if(da[L]>=da[R]) return query(lc);
        else return query(rc);
    }
    void dfss(int u,int fa){
        for(int i=fr[u];i;i=la[i])
          if(to[i]!=fa){
               dfss(to[i],u);
               rt[u]=merge(rt[u],rt[to[i]],1,tot);
          }
        ans[u]=is[query(rt[u],1,tot)];
    }
    struct node{
        int x,y,z;
    }Q[100005];
    inline int as(node x,node y){
        return x.z<y.z;
    }
    int main(){
        int x,y,z,n=read(),m=read();
        for(int i=1;i<n;i++){
            x=read();y=read();
            add(x,y);add(y,x);
        }
        d[1]=1;
        dfs(1,0);
        for(int i=1;i<=m;i++)
          Q[i].x=read(),Q[i].y=read(),Q[i].z=read();
        sort(Q+1,Q+m+1,as);
        for(int i=1;i<=m;i++){
            if(Q[i].z!=Q[i+1].z) is[++tot]=Q[i].z,Q[i].z=tot;
            else Q[i].z=tot+1;
        }
        for(int i=1;i<=m;i++){
            int lca=getlca(Q[i].x,Q[i].y);
            insert(rt[Q[i].x],1,tot,Q[i].z,1);
            insert(rt[Q[i].y],1,tot,Q[i].z,1);
            insert(rt[lca],1,tot,Q[i].z,-1);
            insert(rt[f[lca][0]],1,tot,Q[i].z,-1);
        }
        dfss(1,0);
        for(int i=1;i<=n;i++) printf("%d
    ",ans[i]);
        return 0;
    }
    AC代码

     

     这次考试总体状态还可以,就是对于一些东西想的不深入,没有透彻理解

     

     主要就是第一题没有拿到应该的分,当时感觉是一道水题没有当回事,码的时候没有注意细节,自己编了几个样例过了就以为稳了

     

     以后对于水题一定得仔细,否则别的题得分再高也没用

     

     以上。

    ${color{Teal} 只}$${color{Teal} 是}$${color{Teal} 拼}$${color{Teal} 凑}$${color{Teal} 出}$${color{Teal} 与}$${color{Teal} 你}$${color{Teal} 在}$${color{Teal} 一}$${color{Teal} 起}$${color{Teal} 的}$${color{Teal} 时}$${color{Teal} 间}$
  • 相关阅读:
    Thrust--self-defined(4)
    x86---32汇编(3)---内存寻址模式
    x86---32汇编(2)---局部变量
    x86---32汇编(1)---乘除法
    CUDA--Thrust(3)--List
    CUDA-Thrust(2)--野指针转换
    CUDA-Thrust(1)--函数初始化
    CUDA---Thrust(0)--初始化
    python(13)--Numpy的安装
    20200930 day24 刷题记录
  • 原文地址:https://www.cnblogs.com/mikufun-hzoi-cpp/p/11038262.html
Copyright © 2020-2023  润新知