• 2019.10.29 csp-s模拟测试93 反思总结


    T1:

    求出前缀和,三维偏序O(nlog2n)CDQ

    二维其实就可以

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e6+10,inf=2147483;
    int n,ans,tot,tree[2*N];
    long long a[N],b[N];
    long long suma[N],sumb[N],c[2*N];
    struct node{
        int ha,hb,id;
    }s[N],d[N];
    void add(int x,int k){
        for(;x<=tot;x+=(x&-x))tree[x]=min(tree[x],k);
    }
    void clear(int x){
        for(;x<=tot;x+=(x&-x))tree[x]=inf;
    }
    int ask(int x){
        int num=inf;
        for(;x;x-=(x&-x))num=min(tree[x],num);
        return num;
    }
    void work(int l,int r){
        if(l==r){
            return;
        }
        int mid=(l+r)/2;
        work(l,mid),work(mid+1,r);
        int ll=l,rr=mid+1,p=l;
        while(ll<=mid&&rr<=r){
            if(s[ll].ha<=s[rr].ha){
                add(s[ll].hb,s[ll].id);
                d[p++]=s[ll++];
            }
            else{
                int x=ask(s[rr].hb);
                if(x!=inf)ans=max(ans,s[rr].id-x);
                d[p++]=s[rr++];
            }
        }
        while(rr<=r){
            int x=ask(s[rr].hb);
            if(x!=inf)ans=max(ans,s[rr].id-x);
            d[p++]=s[rr++];
        }
        for(int i=l;i<=ll;i++){
            clear(s[i].hb);
        }
        while(ll<=mid)d[p++]=s[ll++];
        for(int i=l;i<=r;i++)s[i]=d[i];
    }
    int main(){
    //    freopen("sequence.in","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=n;i++){
            scanf("%lld",&b[i]);
        }
        for(int i=0;i<=n;i++){
            if(i)suma[i]=suma[i-1]+a[i];
            if(i)sumb[i]=sumb[i-1]+b[i];
            c[++tot]=suma[i],c[++tot]=sumb[i];
        }
        sort(c+1,c+tot+1);
        tot=unique(c+1,c+tot+1)-c-1;
        for(int i=0;i<=tot;i++)tree[i]=inf;
        for(int i=0;i<=n;i++){
            s[i].ha=lower_bound(c+1,c+tot+1,suma[i])-c;
            s[i].hb=lower_bound(c+1,c+tot+1,sumb[i])-c;
            s[i].id=i;
        }
        work(0,n);
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    一开始没有处理0的位置,出题人慷慨地送了我90pts

    T2:

    区间DP+四边形不等式优化

    DP合并方式是枚举决策点,左右可以看作独立的树,然后再整体加一层的贡献

    发现决策点单调,于是f[i][j]只从p[i][j-1]->p[i+1][j]枚举决策点。p为决策点数组。

    跳过四边形不等式证明:如果觉得决策点单调就把决策点矩阵打印出来,发现行列上均单调,则有可能可以四边形不等式优化。

    O(n2

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const long long inf=1e18;
    int n,a[5010],p[5010][5010];
    long long f[5010][5010],sum[5010];
    int main()
    {
    //    freopen("tree.in","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=i;j<=n;j++){
                f[i][j]=inf;
            }
        }
        for(int i=1;i<=n;i++)f[i][i]=a[i],p[i][i]=i;
        for(int i=2;i<=n;i++){
            for(int j=1;j+i-1<=n;j++){
                for(int k=p[j][j+i-2];k<=p[j+1][j+i-1];k++){
                    if(f[j][k-1]+f[k+1][j+i-1]+sum[j+i-1]-sum[j-1]<f[j][j+i-1]){
                        f[j][j+i-1]=f[j][k-1]+f[k+1][j+i-1]+sum[j+i-1]-sum[j-1];
                        p[j][j+i-1]=k;
                    }
                }
            }
        }
        printf("%lld
    ",f[1][n]);
        return 0;
     } 
    View Code

    T3:

    设从k点开始转移,以1的最终值为答案。

    列出转移方程:f[i]=∑f[j]/x+1,x为i的出度。移项,列出高斯消元数组。

    对于一个点,当它作为k时,它的f值为0。又发现对于不同的k,每次其它点的消元式子并不会发生变化。

    线段树分治,每次存下当前的数组,只消元一半,然后递归进下一层。当处理到只有一个点的区间时,这个点的答案即为消元数组中1号点对应的答案。

    一开始避免了消元时的选行,以及消元的时候出现0似乎没有什么影响:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int mod=998244353;
    int n,m;
    int ver[100010],Next[100010],head[310],tot,chu[310];
    long long val[310][310],ans[310],d[310][310][310];
    void add(int x,int y){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
    }
    long long ks(long long x,int k){
        long long num=1;
        while(k){
            if(k&1)num=num*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return num;
    }
    void make(int l,int r){
    //    int pos;
        for(int i=l;i<=r;i++){
    //        pos=i;
    //        for(int j=i;j<=n;j++){
    //            if(val[j][i]>val[pos][i])pos=j;
    //        }
    //        for(int j=1;j<=n+1;j++){
    //            swap(val[i][j],val[pos][j]);
    //        }
            int tmp=val[i][i];
            if(!tmp)continue;
            long long inv=ks(tmp,mod-2);
            for(int j=1;j<=n+1;j++){
                val[i][j]=val[i][j]*inv%mod;
            }
            for(int j=1;j<=n;j++){
                if(j!=i){
                    int s=val[j][i];
                    for(int k=1;k<=n+1;k++){
                        val[j][k]=(val[j][k]-s*val[i][k]%mod+mod)%mod;
                    }
                }
            }
        }
    } 
    void work(int l,int r,int dep){
        if(l==r){
            ans[l]=val[1][n+1];
            return;
        }
    //    long long d[310][310];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n+1;j++){
                d[dep][i][j]=val[i][j];
            }
        }
        int mid=(l+r)/2;
        make(l,mid);
        work(mid+1,r,dep+1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n+1;j++){
                val[i][j]=d[dep][i][j];
            }
        }
        make(mid+1,r);
        work(l,mid,dep+1);
    }
    int main()
    {
    //    freopen("walk.in","r",stdin);
    //    freopen("1.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            chu[x]++;
        }
        for(int i=1;i<=n;i++){
            val[i][i]=-chu[i];
    //        long long inv=ks(chu[i],mod-2);
            for(int j=head[i];j;j=Next[j]){
                int y=ver[j];
                val[i][y]++;
            }
            val[i][n+1]=-chu[i];
        }
        work(1,n,1);
        for(int i=2;i<=n;i++){
            printf("%lld
    ",ans[i]);
        }
        return 0;
    }
    View Code

    这样选行会错的原因是,对于一个k来说,整个消元过程中它是不能被选择到的。在它作为k的意义下,它的数组其实相当于不存在。

    及时跳出选行即可:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int mod=998244353;
    int n,m;
    int ver[100010],Next[100010],head[310],tot,chu[310];
    long long val[310][310],ans[310],d[310][310][310];
    void add(int x,int y){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
    }
    long long ks(long long x,int k){
        long long num=1;
        while(k){
            if(k&1)num=num*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return num;
    }
    void make(int l,int r){
        int pos;
        for(int i=l;i<=r;i++){
            pos=i;
            for(int j=i;j<=n;j++){
                if(val[j][i]){pos=j;break;}
            }
            for(int j=1;j<=n+1;j++){
                swap(val[i][j],val[pos][j]);
            }
            for(int j=1;j<=n+1;j++)val[i][j]=(val[i][j]+mod)%mod;
            int tmp=val[i][i];
            if(!tmp)continue;
            long long inv=ks(tmp,mod-2);
            for(int j=1;j<=n+1;j++){
                val[i][j]=val[i][j]*inv%mod;
            }
            for(int j=1;j<=n;j++){
                if(j!=i){
                    int s=val[j][i];
                    for(int k=1;k<=n+1;k++){
                        val[j][k]=(val[j][k]-s*val[i][k]%mod+mod)%mod;
                    }
                }
            }
        }
    } 
    void work(int l,int r,int dep){
        if(l==r){
            ans[l]=val[1][n+1];
            return;
        }
    //    long long d[310][310];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n+1;j++){
                d[dep][i][j]=val[i][j];
            }
        }
        int mid=(l+r)/2;
        make(l,mid);
        work(mid+1,r,dep+1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n+1;j++){
                val[i][j]=d[dep][i][j];
            }
        }
        make(mid+1,r);
        work(l,mid,dep+1);
    }
    int main()
    {
    //    freopen("walk.in","r",stdin);
    //    freopen("1.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            chu[x]++;
        }
        for(int i=1;i<=n;i++){
            val[i][i]=-chu[i];
    //        long long inv=ks(chu[i],mod-2);
            for(int j=head[i];j;j=Next[j]){
                int y=ver[j];
                val[i][y]++;
            }
            val[i][n+1]=-chu[i];
        }
        work(1,n,1);
        for(int i=2;i<=n;i++){
            printf("%lld
    ",ans[i]);
        }
        return 0;
    }
    View Code

     ↑上面的复杂度都是O(n3logn)每个点会被消log次,消一次是n2

    //????????????????
    //????????????
    //exm??????
    //??????????????? ?????????
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int mod=998244353;
    int n,m;
    int ver[100010],Next[100010],head[310],tot,chu[310];
    long long val[310][310],ans[310],d[310][310][310];
    void add(int x,int y){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
    }
    long long ks(long long x,int k){
        long long num=1;
        while(k){
            if(k&1)num=num*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return num;
    }
    void make(int l,int r,int L,int R){
        for(int i=l;i<=r;i++){
    //        for(int j=1;j<=n+1;j++)val[i][j]=(val[i][j]+mod)%mod;
            int tmp=val[i][i];
            if(!tmp)continue;
            long long inv=ks(tmp,mod-2);
            for(int j=L;j<=R;j++){
                val[i][j]=val[i][j]*inv%mod;
            }
            val[i][n+1]=val[i][n+1]*inv%mod;
            for(int j=1;j<=n;j++){
                if(j!=i){
                    int s=val[j][i];
                    if(!s)continue;
                    for(int k=L;k<=R;k++){
                        val[j][k]=(val[j][k]-s*val[i][k]%mod+mod)%mod;
                    }
                    val[j][n+1]=(val[j][n+1]-s*val[i][n+1]%mod+mod)%mod;
                }
            }
        }
    } 
    void work(int l,int r,int dep){
        if(l==r){
            ans[l]=val[1][n+1];
            return;
        }
    //    long long d[310][310];
        for(int i=1;i<=n;i++){
            for(int j=l;j<=r;j++){
                d[dep][i][j]=val[i][j];
            }
            d[dep][i][n+1]=val[i][n+1];
        }
        int mid=(l+r)/2;
        make(l,mid,l,r);
        work(mid+1,r,dep+1);
        for(int i=1;i<=n;i++){
            for(int j=l;j<=r;j++){
                val[i][j]=d[dep][i][j];
            }
            val[i][n+1]=d[dep][i][n+1];
        }
        make(mid+1,r,l,r);
        work(l,mid,dep+1);
        for(int i=1;i<=n;i++){
            for(int j=l;j<=r;j++){
                val[i][j]=d[dep][i][j];
            }
            val[i][n+1]=d[dep][i][n+1];
        }
    }
    int main()
    {
    //    freopen("walk.in","r",stdin);
    //    freopen("1.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            chu[x]++;
        }
        for(int i=1;i<=n;i++){
            val[i][i]=-chu[i];
    //        long long inv=ks(chu[i],mod-2);
            for(int j=head[i];j;j=Next[j]){
                int y=ver[j];
                val[i][y]++;
            }
            val[i][n+1]=-chu[i];
        }
        work(1,n,1);
        for(int i=2;i<=n;i++){
            printf("%lld
    ",ans[i]);
        }
        return 0;
    }
    抄思路&&%%%巨神

    ↑这个是O(n3

    为什么????陷入迷惑

  • 相关阅读:
    类的自动载入
    设计模式的概述
    laravel开发微信公众号1 之基本配置
    laravel 中 与前端的一些事5 之解决缓存问题:version
    laravel 中 与前端的一些事4 之合并压缩静态文件
    H3C AR28-31路由器组网实验
    二分查找找一个数所在的范围
    从Code::Blocks到Dev–C++,Dev-C++安装调试一条龙
    Code::Blocks使用与调试一条龙
    JSP相对路径与绝对路径探秘
  • 原文地址:https://www.cnblogs.com/chloris/p/11763315.html
Copyright © 2020-2023  润新知