• 牛客提高组模拟赛4


    牛客提高组模拟赛4

    T1 麻将

    这题应该做法有很多吧,我提供一种奇怪的做法

    将每一行连续的1提出来,形成一个个区间\(l,r\)

    实际是求对于每一\(1\leq l \leq r \leq m\),能覆盖它的\(l,r\)有多少个

    怎么求呢?

    首先我们将这些区间存储在每个左端点上

    循环枚举左端点,每次将右端点的sum++,维护一个后缀即可,累加得到每个右端点的答案

    直接这样做,复杂度应该是\(n \cdot m+m \cdot m\)

    那对于\(m\)较大\(n\)较小的情况,如何优化为\(n*m\)呢?

    其实是非常简单的,记录右端点出现的最远位置\(maxr\),每次从\(maxr\)循环到i即可

    这样的话,其实每一次是循环一个连续区间的块长,总共块长应该是\(n*m\)

    
    
    const int N=5010;
    
    bool be;
    int n,m;
    int a[N][N];
    
    struct Edge{
    	int to,nxt;
    }e[N*N/2];
    int head[N],ecnt;
    void AddEdge(int u,int v){
    	e[++ecnt]=(Edge){v,head[u]};
    	head[u]=ecnt;
    }
    int sum[N],k[N];
    bool ed;
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n) {
    		int c=0,l=1;
    		rep(j,1,m) {
    			char ch;
    			do ch=getchar();
    			while(ch!='1'&&ch!='0');
    			a[i][j]=ch^'0';
    		}
    		rep(j,1,m) {
    			if(a[i][j]) {
    				if(!c) l=j;
    				c++;
    				if(!a[i][j+1]) {
    					AddEdge(l,j);
    					c=0;
    				}
    			} 	
    		}
    	}
    	int ans=0;
    	rep(i,1,m) {
    		int ma=0;
    		for(int j=head[i];j;j=e[j].nxt) {
    			ma=max(ma,e[j].to);
    			k[e[j].to]++;
    		}
    		int d=0;
    		drep(j,ma,i) {
    			d+=k[j],k[j]=0;
    			sum[j]+=d;
    			ans=max(ans,(j-i+1)*sum[j]);
    		}
    	}
    	printf("%d\n",ans);
    }
    

    \[\ \]

    \[\ \]

    \[\ \]

    T2卖羊驼

    dp裸题吧

    \[dp[i][j]$$前j个数,分成$i$段的最大收益 首先我们考虑$n^3$转移 ```cpp for(int k=1;k<j;++k) dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[k][j]) ``` 原理简单,拿到部分分 如何优化呢? 考虑$dp[i-1][k]$的单调非递减性 我们倒着思考这个循环,$dp[i-1][k]$递减,而$sum[k][j]$单调非递增 当$sum[k][j]=sum[k+1][j]$时,这次的转移一定没有$dp[i-1][k]+sum[k+1][j]$优 故只考虑$sum[k][j]>sum[k+1][j]$的情况 而什么时候存在这种情况呢?当且仅当$a[k]$中存在$a[k+1..j]$的二进制位中不拥有的1时递增 所以存下每一位的一上一次出现的位置,一共有$log_2(10^7)$的转移方案 总复杂度$n\cdot k \cdot log_2(10^7)$,略大于一个亿的样子,但实际上这题时限2s,不是特别卡常吧 ```cpp #include<bits/stdc++.h> using namespace std; #define reg register typedef long long ll; #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i) char IO; int rd(){ int s=0,f=0; while(!isdigit(IO=getchar())) if(IO=='-') f=1; do s=(s<<1)+(s<<3)+(IO^'0'); while(isdigit(IO=getchar())); return f?-s:s; } const int N=5100; int n,k; int a[N],sum[N][N]; ll dp[N/5][N]; int pre[26]; inline int max(int a,int b){ return a>b?a:b; } int main(){ n=rd(),k=rd(); rep(i,1,n) a[i]=rd(); rep(i,1,n) rep(j,i,n) sum[i][j]=sum[i][j-1]|a[j]; ll ans=0; rep(i,1,n) dp[1][i]=sum[1][i]; rep(i,2,k) { memset(pre,-1,sizeof pre); rep(j,1,n) { rep(o,0,23) { if(a[j]&(1<<o)) pre[o]=j; if(~pre[o]) { dp[i][j]=max(dp[i][j],dp[i-1][pre[o]-1]+sum[pre[o]][j]); } } ans=max(ans,dp[i][j]); } } printf("%lld\n",ans); } ``` $$ \ \]

    \[\ \]

    \[\ \]

    T3 清(素)新(质)题

    这不是线性基裸题吗。。

    我就不提供代码了

    我大概有三种办法写这题

    ​ 1.DSU+线性基

    ​ 2.线性基合并,收集子树信息

    ​ 3.转化为序列问题,维护最优性线性基,保证线性基里的数出现的位置最靠近右端点即可

    但是复杂度均为\(log^2n\),并不是特别优秀

    比赛时敲的DSU,感觉没什么细节

    const int N=1e5+10;
    bool be; 
     
    int n;
    struct Edge{
        int to,nxt;
    }e[N<<1];
    int head[N],ecnt;
    void AddEdge(int u,int v){
        e[++ecnt]=(Edge){v,head[u]};
        head[u]=ecnt;
    }
    #define erep(u,i) for(int i=head[u];i;i=e[i].nxt)
     
    int a[N];
    int sz[N],son[N],L[N],R[N],dfn,id[N];
    void pre_dfs(int u,int f){
        id[L[u]=++dfn]=u;
        sz[u]=1;
        erep(u,i) {
            int v=e[i].to;
            if(v==f) continue;
            pre_dfs(v,u);
            sz[u]+=sz[v];
            if(sz[son[u]]<sz[v]) son[u]=v;
        }
        R[u]=dfn;
    }
     
    struct Linear_Basis{
        int d[20];
        int Add(int x){
            drep(i,17,0) if(x&(1<<i)) {
                if(d[i]) x^=d[i];
                else {
                    d[i]=x;
                    return 1;
                }
            }
            return 0;
        }
        int Quemax() {
            int ans=0;
            drep(i,17,0) if((ans^d[i])>ans) ans^=d[i];
            return ans;
        }
        void init(){ memset(d,0,sizeof d); }
    } B;
     
    int ans[N];
    void Dsu(int u,int f){
        erep(u,i) {
            int v=e[i].to;
            if(v==f||v==son[u]) continue;
            Dsu(v,u);
            B.init();
        }
        if(son[u]) Dsu(son[u],u);
        B.Add(a[u]);
        erep(u,i) {
            int v=e[i].to;
            if(v==f||v==son[u]) continue;
            rep(j,L[v],R[v]) B.Add(a[id[j]]);
        }
        ans[u]=B.Quemax();
    }
    
    bool ed;
     
    int main(){
    	//printf("%lf\n",(&ed-&be)/1024.0/1024.0);
        rep(i,2,n=rd()) {
            int u=rd(),v=rd();
            AddEdge(u,v);
            AddEdge(v,u);
        }
        rep(i,1,n) a[i]=rd();
        pre_dfs(1,0);
        Dsu(1,0);
        rep(i,1,rd()) printf("%d\n",ans[rd()]);
    }
    
    
  • 相关阅读:
    课后作业-阅读任务-阅读笔记-4
    团队编程项目作业5-小组评分
    课后作业-阅读任务-阅读提问-3
    20171110-构建之法:现代软件工程-阅读笔记
    团队-中国象棋-开发文档
    结对-贪吃蛇-结对项目总结
    20171129-构建之法:现代软件工程-阅读笔记
    课后作业-阅读任务-阅读提问-4
    软件工程课程总结
    团队-石头剪刀布-项目总结
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11393835.html
Copyright © 2020-2023  润新知