zoukankan      html  css  js  c++  java
  • Codeforces Round #683 (Div. 2, by Meet IT)

    Codeforces Round #683 (Div. 2, by Meet IT)

    A,B过水。

    C:给20w个数,给你一个数K,问你能不能在这20w个数里,找到若干个数使得它们的和超过ceil[K/2],且不超过K。

    题解:如果20w个数中,有一个满足的,那直接白给。否则,在小于ceil[k/2]的数中,你直接往上加,如果满足,就输出。加不够则不满足。你可以一个一个加的原因就在于,两个小于ceil(K/2)的数加起来是不会超过K的。我写的时候,加了个排序,属实没必要。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 200005;
    
    #define fi first
    #define se second
    
    typedef pair<int,int> PII;
    
    typedef long long LL;
    
    int n;
    
    LL w; 
    
    PII a[N];
    
    int main(){
    	int _;cin>>_;
    	while(_--){
    		cin>>n>>w;
    		LL s=0;
    		for(int i=1;i<=n;++i){
    			int x;cin>>x;
    			s+=x;
    			a[i]=make_pair(x,i);
    		}
    		sort(a+1,a+n+1);
    		if(s<(1+w)/2){
    			cout<<"-1"<<endl;
    			continue;
    		}else{
    			int idx=n;
    			while(idx>0&&a[idx].fi>w)--idx;
    			vector<int> v;
    			s=0;
    			for(int i=idx;i>=1;--i){
    				s+=a[i].fi;
    				v.push_back(a[i].se);
    				if(s>=(w+1)/2)break;
    			}
    			if(s<(w+1)/2){
    				cout<<"-1"<<endl;
    				continue;
    			}
    			cout<<v.size()<<endl;
    			for(auto& x:v){
    				cout<<x<<" ";
    			}
    			cout<<endl;
    		}
    	}
    	
    	
    	return 0;
    }
    

    D:给你两个字符串,你可以选取这两个字符串的字串。并且计算它们的分数,分数的定义是,最长公共子序列*4-两个子串的长。

    题解:定义(dp[i][j])为,以i结尾的a串,以j结尾的b串所对应的最大分数。其实它的转移是比较显然的。a[i]==b[j]时,(dp[i][j]=2+max(dp[i-1][j-1],0))。否则,(dp[i][j]=max(-2,dp[i-1][j-1]-2))。当然,对于所有情况(dp[i][j]=max(dp[i-1][j],dp[i][j-1])-1)的转移都是成立的。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 5005;
    
    char a[N], b[N];
    
    int dp[N][N];
    
    int n, m;
    
    void chmax(int& x,int y){
    	if(y>x)x=y;
    }
    
    int main(){
    	cin>>n>>m;
    	cin>>a+1>>b+1;
    	
    	int ans=0;
    	memset(dp,-0x3f,sizeof dp);
    	dp[0][0]=0;
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			if(a[i]==b[j]){
    				chmax(dp[i][j],dp[i-1][j-1]+2);
    				chmax(dp[i][j],2);
    			}else{
    				chmax(dp[i][j],dp[i-1][j-1]-2);
    				chmax(dp[i][j],-2);
    			} 
    			chmax(dp[i][j],dp[i-1][j]-1);
    			chmax(dp[i][j],dp[i][j-1]-1);
    		}
    	}
    	
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			chmax(ans,dp[i][j]);
    		} 
    	}
    	
    	cout<<ans; 
    	
    	return 0;
    } 
    

    E: 给你n个不同的数,每一个数会与和自己xor值最小的数连边,重边算一条。问你最少删除多少个数字,使得它们形成的无向图是一棵树。

    题解:建立01trie,从高位向低位考虑,如果某一个节点的两颗子树size都大于2的话,那显然是不行的。因为它们肯定会选择自己子树中的数字组成最小的xor值。再考虑到,由于n个数中,存在一个客观上的最小xor值,所以必有两个数互相连。这说明我们的图,最多只有n-1条边。此时想成为一棵树,只需要这个无向图是联通的就可以了。如果我们让01trie任意一层的子树,不存在两个子树size都大于2。那么就可行了。因为每次剩下最多一个(选择其中一个子树,使其size为1),会连向其他的连通块,这样会构成一种合法的方案。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 200005;
    
    int n;
    
    int ans;
    
    int trie[N<<5][2], cntNode, cnt[N<<5];
    
    void Insert(int x){
        int cur=0;
        for(int i=30;i>=0;--i){
            if(!trie[cur][x>>i&1]){
                trie[cur][x>>i&1]=++cntNode;
            }
            cur=trie[cur][x>>i&1];
            ++cnt[cur];
        }
    }
    
    void dfs2(int nd, int dep, int cst){
        if(dep==30){
            ans=min(ans,cst);
            return;
        }
    
        if(!trie[nd][0]){
            dfs2(trie[nd][1],dep+1,cst);
        }else if(!trie[nd][1]){
            dfs2(trie[nd][0],dep+1,cst);
        }else{
            dfs2(trie[nd][0],dep+1,cst+cnt[trie[nd][1]]-1);
            dfs2(trie[nd][1],dep+1,cst+cnt[trie[nd][0]]-1);
        }
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            int x;scanf("%d",&x);
            Insert(x);
        }
    
        ans=n;
    
        dfs2(0,0,0);
    
        printf("%d",ans);
    
        return 0;
    }
    

    F: 给你n个数,你需要找到一个最长的区间,满足的条件是里面的众数不止一个。

    题解:首先有一个观察,对于这样的一个最大区间,区间众数肯定是在其中的(如果整个区间的区间众数不止一个,那直接白给),这个比较显然,考虑答案区间有一个替换更优的性质。对于easy version,这个时候已经可以开始枚举是哪个数了,因为不同的数最多100个。但是这个方法不足以解决hard version。

    考虑出现次数比较多的数字,比如出现次数大于sqrt(n)的数字。可以直接考虑枚举这个数,可以发现这样的数不超过sqrt(n)个。

    对于出现次数比较少的数字,比如出现次数小于sqrt(n)的数字。由于它要成为区间众数,所以出现次数也不会特别多,上限是sqrt(n)次。所以我们直接枚举出现次数,尺取答案就好。

    #include<bits/stdc++.h>
     
    using namespace std;
     
    const int N = 200005;
     
    int n, mx, big, a[N], cnt[N], lst[N<<1], processed[N];
     
    int solve1(int num){
        memset(lst,-1,sizeof lst);
     
        int res=0;
     
        int now=0;
        lst[now+n]=0;
        for(int i=1;i<=n;++i){
            if(a[i]==big)++now;
            if(a[i]==num)--now;
            if(lst[now+n]!=-1){
                res=max(res,i-lst[now+n]);
            }
            if(lst[n+now]==-1){
                lst[n+now]=i;
            }
        }
     
        return res;
    }
     
    int solve2(int limit){
        int res=0;
        int eql=0;
        memset(cnt,0,sizeof cnt);
        for(int i=1,j=1;i<=n;++i){
            ++cnt[a[i]];
            if(cnt[a[i]]==limit)++eql;
            while(j<=i&&cnt[a[i]]>limit){
                --cnt[a[j]];
                if(cnt[a[j]]==limit-1)--eql;
                ++j;
            }
     
            if(eql>=2){
                res=max(res,i-j+1);
            }
        }
     
        return res;
    }
     
    int main(){
        scanf("%d",&n);
     
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
            ++cnt[a[i]];
            if(cnt[a[i]]>mx){
                mx=cnt[a[i]];
                big=a[i];
            }
        }
     
        int ans=0;
     
        int up=sqrt(n);
        for(int i=1;i<=n;++i){
            if(cnt[a[i]]>=up&&a[i]!=big&&!processed[a[i]]){
                processed[a[i]]=1;
                ans=max(ans,solve1(a[i]));
            }
        }
     
        for(int i=1;i<up;++i)ans=max(ans,solve2(i));
     
        cout<<ans;
     
        return 0;
    }
    
  • 相关阅读:
    动手动脑——异常处理
    JAVA异常处理总结
    打印沙漏
    暑假第八周进度报告
    暑假第七周进度报告
    暑假第六周进度报告
    暑假第五周进度报告
    暑假第四周进度报告
    暑假第三周进度报告
    暑假第二周进度报告
  • 原文地址:https://www.cnblogs.com/JohnRan/p/14025558.html
Copyright © 2011-2022 走看看