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;
}
原文地址:https://www.cnblogs.com/JohnRan/p/14025558.html