zoukankan      html  css  js  c++  java
  • 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

    为什么????陷入迷惑

  • 相关阅读:
    48. Rotate Image
    47. Permutations II
    46. Permutations
    45. Jump Game II
    44. Wildcard Matching
    43. Multiply Strings
    42. Trapping Rain Water
    41. First Missing Positive
    40. Combination Sum II
    39. Combination Sum
  • 原文地址:https://www.cnblogs.com/chloris/p/11763315.html
Copyright © 2011-2022 走看看