2021牛客暑期多校训练营5

2021牛客暑期多校训练营5

B:Boxes

解释

贪心可知,最多使用一次C,多次使用效果一样,早使用不比晚使用差。优先开权值小的更优。

对于每一个箱子的期望,只有它后面是全黑或者全白时,才没有贡献。

所以答案为 ∑ i = 1 n w i × ( 1 − 1 2 n − i ) \sum_{i=1}^{n}w_i\times (1-\frac{1}{2^{n-i}}) i=1nwi×(12ni1)

代码

int n;
double c,sum = 0; cin>>n>>c;
for(int i=1;i<=n;i++) cin>>w[i],sum += w[i];
sort(w+1,w+n+1);
p[0] = 1;
for(int i=1;i<=n;i++) p[i] = p[i-1]*0.5;
for(int i=0;i<n;i++) ans += (1.0-2*p[n-i])*w[i+1];
cout<<fixed<<setprecision(6)<<min(ans+c,sum)<<endl;

D:Double_Strings

解释

题目得出: 一段相同的前缀 + 一个不同字符(a比b小)+ 长度相同的任意后缀 就是合法方案。

可以用dp求得前面,中间通过枚举,后面可以用组合数的方法解决。

前面的问题转换为求公共子序列的个数

d p ( i , j ) dp(i,j) dp(i,j) 表示a串前i个和b串前j个相同子序列的个数

转移方程:

d p ( i , j ) = d p ( i − 1 , j ) + d p ( i , j − 1 ) − d p ( i − 1 , j − 1 ) dp(i,j) = dp(i-1,j) + dp(i,j-1) - dp(i-1,j-1) dp(i,j)=dp(i1,j)+dp(i,j1)dp(i1,j1)

s ( i ) = = s ( j ) s(i) == s(j) s(i)==s(j)

d p ( i , j ) + = d p ( i − 1 , j − 1 ) + 1 dp(i,j) += dp(i-1,j-1) + 1 dp(i,j)+=dp(i1,j1)+1

后面问题

假设有a串此时还剩下x个,b串还剩下y个。 令 x <= y,则答案为 ∑ l e n = 0 x C x l e n × C y l e n = ∑ l e n = 0 x C x x − l e n × C y l e n = ∑ l e n = 0 x C x + y x \sum_{len=0}^{x}C_{x}^{len}\times C_{y}^{len} = \sum_{len=0}^{x}C_{x}^{x-len}\times C_{y}^{len} = \sum_{len=0}^{x}C_{x+y}^{x} len=0xCxlen×Cylen=len=0xCxxlen×Cylen=len=0xCx+yx

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 10;
const int mod = 1e9 + 7;
#define int long long
typedef pair<int,int> pii;
const int INF = 1e18;

int dp[N][N];
int fact[N*2],infact[N*2];

int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

void init(int n,int m){
	fact[0] = infact[n+m] = 1;
	for(int i=1;i<=n+m;i++) fact[i] = fact[i-1] * i % mod;
	infact[n+m] = qmi(fact[n+m],mod-2);
	for(int i=n+m-1;i>=0;i--) infact[i] = infact[i+1] * (i+1) % mod;
}

int C(int n,int m){
	return fact[n] * infact[m] % mod * infact[n-m] % mod;
}

signed main(){
	IOS
    string a,b; cin>>a>>b;
    int n = (int)a.size(),m = (int)b.size();
    init(n,m);
    for(int i=0;i<=max(n,m);i++) dp[i][0] = dp[0][i] = 1;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++){
    		dp[i][j] = (dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mod) % mod;
    		if(a[i-1] == b[j-1]) dp[i][j] = (dp[i][j] + dp[i-1][j-1]) % mod;
		}
	int ans = 0;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    		if(a[i-1] < b[j-1]) ans = (ans + dp[i-1][j-1] * C(n+m-i-j,n-i) % mod) % mod;
    cout<<ans<<endl;
    return 0;
}

H:Holding_Two

解释

10101010

10101010

01010101

01010101

如上构造,合法

代码

int n,m; cin>>n>>m; 
string s = "",p = "";
for(int i=0;i<m;i++){
    if(i&1) s += '1',p += '0';
    else s += '0',p += '1';
}
for(int i=0;i<n;i++){
    if((i/2)&1) cout<<s<<endl;
    else cout<<p<<endl; 
}

J:Jewels

解释

每一秒都会拿一个宝珠,在0 ~ n-1秒能够全部拿完,然后每一个宝珠在0 ~ n-1秒都会对应一个位置。

用二分图带权最小匹配可以得到答案。用BFS版本的KM算法。

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 3e2 + 10;
const int mod = 998244353;
#define int long long
typedef pair<int,int> pii;
const int INF = 1e18;

int w[N][N],match[N],n;
int la[N],lb[N];//左、右部点的顶标
bool va[N],vb[N];//访问标记,是否在交错树中
int delta,upd[N],p[N],c[N];

void bfs(int x){
    int a,y = 0,y1 = 0;
    for(int i=1;i<=n;++i) p[i] = 0,c[i] = INF;
    match[y] = x;
    do{
        a = match[y], delta = INF, vb[y] = true;
        for(int b=1;b<=n;++b){
            if(!vb[b]){
                if(c[b] > la[a] + lb[b] - w[a][b])
                    c[b] = la[a] + lb[b] - w[a][b], p[b] = y;
                if(c[b] < delta)// 还是取最小的
                    delta = c[b], y1 = b;
            }
        }
        for(int b = 0; b <= n; ++ b)
            if(vb[b]) la[match[b]] -= delta,lb[b] += delta;
            else c[b] -= delta;
        y = y1;
    }while(match[y]);
    while(y)match[y] = match[p[y]], y = p[y];
}

int KM(){
    for(int i=1;i<=n;++i) match[i] = la[i] = lb[i] = 0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j) vb[j] = false;
        bfs(i);
    }
    int res = 0; //若匹配失败w[match[y]][y]=INF;
    for(int y=1;y<=n;++y) res += w[match[y]][y];
    return res;
}

int distance(int x,int y,int z){
	return x*x + y*y + z*z;
}

signed main(){
	IOS
    cin>>n;
    for(int i=1;i<=n;i++){
    	int x,y,z,v; cin>>x>>y>>z>>v;
    	for(int j=1;j<=n;j++)
    		w[i][j] = -(distance(x,y,z+v*(j-1)));
	}
    cout<<-KM()<<endl;
    return 0;
}

K:King_of_Range

解释

求数组中有多少对 (l,r) 使得l到r中的元素,满足最大值-最小值 > k。

双指针+单调队列

枚举r,维护单调递增序列和单调递减序列。这样可以得到以r为右端点,l为左端点时,区间最大值和最小值,此时产生的贡献为 n − r + 1 n-r+1 nr+1​ 当有满足的时候,左端点往右移。

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N = 1e5 + 10,M = 300;
typedef long long ll;

int a[N];

signed main(){
	IOS
    int n,m; cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
    	int k; cin>>k;
    	ll ans = 0;
    	deque<int> big,small;
    	for(int r=1,l=1;r<=n;r++){
    		while(big.size() && a[big.back()] <= a[r]) big.pop_back();
    		big.push_back(r);
    		while(small.size() && a[small.back()] >= a[r]) small.pop_back();
    		small.push_back(r);
    		while(big.size() && small.size() && a[big.front()] - a[small.front()] > k){
    			ans += n - r + 1;
    			if(big.front() <= l) big.pop_front();
    			if(small.front() <= l) small.pop_front();
    			l ++;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值