2021牛客暑期多校训练营5

出题数:5
排名:180

H.Holding Two

WYX
题意:
输出一个n*m的矩阵,矩阵中满足在横行,竖行,斜行中,任意的连续的3个的元素不能都为0,或都为1。
题解:
构造一个这样的矩阵
0 0 1 1 0 0 1 1 ……
1 1 0 0 1 1 0 0 ……
0 0 1 1 0 0 1 1 ……
1 1 0 0 1 1 0 0 ……
0 0 1 1 0 0 1 1 ……

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;

int main() 
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);  
        
            int n,m;cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if((j/2)&1) cout<<(0^(i&1));
                else cout<<(1^(i&1));
            }
            cout<<endl;
        }
 	return 0;
}

B.Boxes

WXL+CY
在这里插入图片描述

#include <bits/stdc++.h>
typedef long long ll;
const int MAXN = 1e5 + 10;
double w[MAXN], C, pre[MAXN], suf[MAXN];
int n;
double sum = 0;
int main()
{
    scanf("%d%lf", &n, &C);
    for (int i = 1; i <= n; ++i)
    {
    	scanf("%lf",&w[i]);
    	sum += w[i];
    }
    std::sort(w + 1, w + 1 + n);
    double p = 0.5, ans = C;
    for (int i = n-1; i >= 1; --i)
    {
        ans += p * w[i];
        p  *= 0.5;
        p += 0.5;
    }
    if(sum <= ans)
    {
    	printf("%.10lf\n",sum);
    }
    else
    {
    	printf("%.10lf\n", ans);
    }
    return 0;
}

K.King of Range

WYX
题意
给你一个长为n的数组,有m次询问,每次询问给你一个k,问有多少对(l,r),使数组区间l到r的最大值与最小值的差大于k

WYX写的线段树,应该不用这么麻烦

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+10;
int a[maxn],c[maxn];
int tree[maxn][2];
void build(int l,int r,int x)      //建树,x为编号,l,r为其表示的区间
{
	if(l==r) {
		tree[x][0]=a[l];
        tree[x][1]=a[l];    
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,x<<1);
	build(mid+1,r,x<<1|1);
	tree[x][0]=min(tree[x<<1][0],tree[x<<1|1][0]);
    tree[x][1]=max(tree[x<<1][1],tree[x<<1|1][1]);
}
int query(int l,int r,int x,int st,int ed)     //min
{                                                          //查询操作 ,st与ed为查询区间
    if(st<=l&&r<=ed) return tree[x][0];
    int mid=(l+r)/2,ans=1e9+10;
    if(st<=mid) ans=query(l,mid,x<<1,st,ed);   //x<<1表示x*2
    if(ed>mid)  ans=min(ans,query(mid+1,r,x<<1|1,st,ed));  
    return ans;
}
int query1(int l,int r,int x,int st,int ed)    
{                                                          //查询操作 ,st与ed为查询区间
    if(st<=l&&r<=ed) return tree[x][1];
    int mid=(l+r)/2,ans=0;
    if(st<=mid) ans=query1(l,mid,x<<1,st,ed);   //x<<1表示x*2
    if(ed>mid)  ans=max(ans,query1(mid+1,r,x<<1|1,st,ed));  
    return ans;
}
int f[maxn][50],lg2[maxn];     //log2[x]表示对log2(x)向下取整 
int f1[maxn][50];
void init(int k,int n)                               //k=log2(maxn) 
{
	for(int i=1;i<=n;i++)
	f[i][0]=a[i],f1[i][0]=a[i];    //a为输入的数组

	for(int j=1;j<k;j++) 
	for(int i=1;i+(1<<j)-1<=n;i++)
	{
        f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        f1[i][j]=min(f1[i][j-1],f1[i+(1<<(j-1))][j-1]);
    }
	lg2[1]=0;
	for(int i=2;i<=n;i++)    //预处理log
	lg2[i]=lg2[i/2]+1;
}
int st(int l,int r)
{
	int s=lg2[r-l+1];
	return max(f[l][s],f[r-(1<<s)+1][s]);
} 
int st1(int l,int r)
{
	int s=lg2[r-l+1];
	return min(f1[l][s],f1[r-(1<<s)+1][s]);
} 
int main() 
{
    // ios_base::sync_with_stdio(false);
    // cin.tie(nullptr);
    // cout.tie(nullptr);  
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);//build(1,n,1);
    init(30,n);
    while(m--)
    {
        int k; scanf("%d",&k);
        ll ans=0;
        int l=1;
        for(int i=1;i<=n;i++)
        {
            //while(l<=i&&query1(1,n,1,l,i)-query(1,n,1,l,i)>k) l++;
            while(l<=i&&st(l,i)-st1(l,i)>k) l++;
            ans+=i-l+1;
        }
        ans=1ll*n*(n+1)/2-ans;
        printf("%lld\n",ans);
    }
 	return 0;
}

/*

3 1
1 5 1
3

*/

这里还有一个双指针的做法:
若给你个子数组发现最大值与最小值的差已经满足条件,那么是不是意味着在这个子数组的基础上在添加其他的元素,其最大值与最小值的差也会成立。所以我们可以用双子针,固定i的位置,来找最小的j。要在一个区间内找最小值,我们可以用st表。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;    while (b) { if (b & 1)    ans *= a;        b >>= 1;        a *= a; }    return ans; }   
ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }

const int mod=1e9+7;
const int N=1e5+7;

int n,m,l,r;
int Log[N]; // 用来求log的
int stmax[N][22];
int stmin[N][22];

void fun(){//求log的函数
    Log[1]=0;
    for(int i=2;i<=n;i++)
        Log[i]=Log[i/2]+1;
}

bool search(int l,int r,ll k){//判断区间l到r是否可以
     int s=Log[r-l+1]; //求log的值
     int ma=max(stmax[l][s], stmax[r-(1<<s)+1][s]);//区间最大值
     int mi=min(stmin[l][s], stmin[r-(1<<s)+1][s]);//区间最小值
     if(ma-mi>k) return true; //判断是否大于k
     else return false;
}

int main(){
    while(cin>>n>>m){
        fun();
        for(int i=1;i<=n;i++) {cin>>stmax[i][0];stmin[i][0]=stmax[i][0];}
        for(int j=1;j<=21;j++)//构造st表
            for(int i=1;i+(1<<j)-1<=n;i++){
                stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
                stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<(j-1))][j-1]);
            }

        while(m--){
            ll k,ans=0;
            cin>>k;
            for(int i=1,j=1;i<=n;i++){
                while(!search(i,j,k)&&j<=n&&i<=j){//固定i的值,找到成立的j的最小值
                    j++;
                }
                if(j<=n) ans=ans+n-j+1; //如果某一子段可以,则无论后面加上什么数形成的数组都可以
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

D.Double Strings

CY
dp

#include <bits/stdc++.h>
typedef long long ll;
const int MAXN = 5e3 + 10, MOD = 1e9 + 7;
int dp1[MAXN][MAXN], dp2[MAXN][MAXN];
char a[MAXN], b[MAXN];
int main()
{
    scanf("%s%s", a + 1, b + 1);
    int n = strlen(a + 1), m = strlen(b + 1);
    for (int i = 0; i <= m; ++i)
        dp1[0][i] = 1;
    for (int i = 0; i <= n; ++i)
        dp1[i][0] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            dp1[i][j] = ((dp1[i - 1][j] + dp1[i][j - 1] - dp1[i - 1][j - 1]) % MOD + MOD) % MOD;
            if (a[i] == b[j]) dp1[i][j] = (dp1[i][j] + dp1[i - 1][j - 1]) % MOD;
        }
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            dp2[i][j] = (dp2[i - 1][j] + dp2[i][j - 1]) % MOD;
            if (a[i] < b[j]) dp2[i][j] = (dp2[i][j] + dp1[i - 1][j - 1]) % MOD;
        }
    printf("%d\n", dp2[n][m]);
    return 0;
}

J.Jewels

WXL+WYX
题意:
打捞 nn 个货物,货物会动,每一个时刻只能打捞一个,货物会动,打捞的代价是三维空间上的距离平方。
n 个货物只需要 [ 0 , n − 1 ] [0, n - 1] [0,n1]时刻就能全部打捞,对于每个货物在每一个时刻都有相应的代价,可以构建二分图,左边是时刻,右边是货物,做带权二分图的最优匹配(KM算法)即可。
在这里插入图片描述

ps:刚开始WYX用的EK算法TLE了,使用KM算法才过
复杂度 O ( n 3 ) O(n^3) O(n3)

/*
 * @Author: Kurisu
 */
#include<bits/stdc++.h>

const int mod = 998244353;
const int N = 2e5 + 5;

long long x[305], y[305], z[305], v[305]; 


long long w[305][305]; // cost matrix 
namespace KM {
	long long cal(int n, int m) {
		std::vector<long long> u(n + 1), v(m + 1), p(m + 1), way(m + 1);
		for (int i = 1; i <= n; i++) {
			p[0] = i;
			long long j0 = 0;
			std::vector<long long> minv(m + 1, 1e18);
			std::vector<char> used(m + 1, false);
			do {
				used[j0] = true;
				long long i0 = p[j0], delta = 1e18, j1;
				for (int j = 1; j <= m; ++j) {
					if (!used[j]) {
						long long cur = w[i0][j] - u[i0] - v[j];
						if (cur < minv[j]) {
							minv[j] = cur, way[j] = j0;
						}
						if (minv[j] < delta) {
							delta = minv[j], j1 = j;
						}
					}
				}
				for (int j = 0; j <= m; ++j) {
					if (used[j]) {
						u[p[j]] += delta, v[j] -= delta;
					}
					else {
						minv[j] -= delta;
					}
				}
				j0 = j1;
			} while (p[j0] != 0);
			do {
				long long j1 = way[j0];
				p[j0] = p[j1];
				j0 = j1;
			} while (j0);
		}
		long long res = 0;
		for (int i = 1; i <= m; i++) {
			res += w[p[i]][i];
		}
		return res;
	}
}

long long get_cost(int id, int t) {
	return x[id] * x[id] + y[id] * y[id] + 1LL * (z[id] + v[id] * t) * (z[id] + v[id] * t);
}


void solve() {
	int n; std::cin >> n;
	for (int i = 1; i <= n; i++) {
		std::cin >> x[i] >> y[i] >> z[i] >> v[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= n - 1; j++) {
			w[i][j + 1] = get_cost(i, j);
		}
	}
	std::cout << KM::cal(n, n) << '\n';
}

signed main() {
	std::cin.sync_with_stdio(false), std::cin.tie(nullptr);
	int T = 1; //std::cin >> T;
	while (T--) {
		solve();
	} 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值