暑假集训日记——8.6(codeforce)

本文记录了四道编程竞赛中的难题,包括寻找字符串S最多T子串的贪心策略,处理金钱变化的简单模拟,求解最大cost的动态规划方法,以及通过排序操作匹配数组的树状数组解决方案。详细题解揭示了解题思路和关键算法。
摘要由CSDN通过智能技术生成

D. Suitable Replacement
题意:把 S串中的?转化为小写字母,使得字符串 S拥有最多的 字符串T的不相交字串,(S中的字母可以互换顺序)
题解:贪心

#include<bits/stdc++.h>
#define mp make_pair
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;


const int N=1e7+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,t;
char a[N],b[N],c[N];
int num[N];

int main()
{
    scanf("%s",a);
    scanf("%s",b);
    int len1=strlen(a),len2=strlen(b);
    int cnt=0;
    for(int i=0;i<len1;i++)
    {
        if(a[i]=='?') cnt++;
        else num[a[i]-'a']++;
    }
    int p=0,flag=0;
    while(1)
    {
        for(int i=0;i<len2;i++)
        {
            if(num[b[i]-'a']>0)
                num[b[i]-'a']--;
            else
            {
                if(cnt>0)
                {
                    cnt--;
                    c[p++]=b[i];
                }
                else
                flag=1;
            }
        }
        if(flag==1) break;
    }
    p=0;
    for(int i=0;i<len1;i++)
    {
        if(a[i]=='?') printf("%c",c[p++]);
        else printf("%c",a[i]);
    }
}

D. Welfare State
题意:
有 n个人,下面一行是每个人的钱,一共 q个操作。
1 操作:是把第i个人的钱数变成 x x x
2 操作:是把所有钱数小于 x x x的变成 x x x
题解:简单模拟,详见代码

#include<bits/stdc++.h>

#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;


const int N=1e7+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,t;
int a[N],b[N];
pair<int,pii>p[N];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p[i].fi);
        if(p[i].fi==1)
            scanf("%d%d",&p[i].se.fi,&p[i].se.se);
        else
            scanf("%d",&p[i].se.se);
    }
    int temp=0;
    ///因为处理的是最后一次,又因为 1操作后的二操作会影响 a[i]的值所以选择倒序
    for(int i=m;i>=1;i--)
    {
        if(p[i].fi==1&&b[p[i].se.fi]==0)///仅处理 1操作的最后一次即可
        {
            b[p[i].se.fi]=1;
            a[p[i].se.fi]=max(temp,p[i].se.se);
        }
        if(p[i].fi==2)///找到最大二操作
        {
            temp=max(temp,p[i].se.se);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(b[i]!=1) a[i]=max(temp,a[i]);
        printf("%d ",a[i]);
    }
}

D. Yet Another Subarray Problem
题意:
给出一列数组和两个常数 m , k m,k mk,然后定义一段子序列的 c o s t cost cost等于该段子序列各元素之和减去该段长度与 m m m之商的向上取整的值与 k k k的乘积。求任取一段连续子序列所能得到的最大的 c o s t cost cost。( p s ps ps:可以取空集,此时子序列的 c o s t cost cost 0 0 0
题解:(动态规划)
d p [ i ] dp[i] dp[i]表示,以 i i i结尾的连续子序列的 c o s t cost cost
dp[i]有两种转移方式:
第一种是:当 ( l − r ) (l-r) (lr)<= m m m
( [ i − x , i ] [i-x,i] [ixi]的区间和) − - k k k 的最大值 ( 0 (0 (0<= x x x<= m ) m) m)
第二种是:当 ( l − r ) (l-r) (lr)> m m m
( [ i − m , i ] [i-m,i] [imi]的区间和) − - k k k + + +(以 i − m i-m im结尾的连续子序列的 c o s t cost cost值)

#include<bits/stdc++.h>

#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;


const int N=10e5+10;
const int MAXN=20010;
const ll MAX=2e18;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
ll n,m,x,y,t,k,p,ans;
ll dp[N],a[N],sum[N];

int main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    memset(dp,-INF,sizeof(dp));
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=max(1ll,i-m+1);j<=i;j++)
            dp[i]=max(dp[i],sum[i]-sum[j-1]-k);
        dp[i]=max(dp[i],dp[max(0ll,i-m)]+sum[i]-sum[max(0ll,i-m)]-k);
 		ans=max(ans,dp[i]);
    }
    printf("%lld",ans);
}

D. Subarray Sorting
题意:
给定一个数组 a 1 a 2 … a n a_1 a_2…a_n a1a2an和一个数组 b 1 b 2 … b n b_1 b_2…b_n b1b2bn
对于一个操作,您可以按非递减顺序对数组 a a a的任何子数组 a [ l … r ] a[l…r] a[lr]排序。
例如,如果 a = [ 4 , 2 , 2 , 1 , 3 , 1 ] a=[4,2,2,1,3,1] a=[4,2,2,1,3,1],然后选择 s u b b a r r a y subbarray subbarray a [ 2 … 5 ] a[2…5] a[25],那么数组就变成 [ 4 , 1 , 2 , 2 , 3 , 1 ] [4,1,2,2,3,1] [4,1,2,2,3,1]
要求您确定是否可以通过对数组 a a a应用任意次数的这个操作(可能为零)来获得数组 b b b
题解:(排序的本质就是两两交换)树状数组求解区间最值
如果 b i b_i bi可以通过排序操作取得, b i b_i bi那么一定是某段区间的最小值,所以接下来就确定这个区间的范围,如果前 i − 1 i-1 i1项都匹配好了,易知这个范围大致就是 [ i , a n s ] [i,ans] [i,ans] a n s ans ans= ( b i (b_i (bi a a a中第一个未匹配的下标 ) ) )
例如题目给的例子, b 2 b_2 b2的范围就是 [ 2 , 4 ] [2,4] [2,4],然后将数组 a = [ 4 , 2 , 2 , 1 , 3 , 1 ] a=[4,2,2,1,3,1] a=[4,2,2,1,3,1],改为 a = [ 4 , 1 , 2 , 2 , 3 , 1 ] a=[4,1,2,2,3,1] a=[4,1,2,2,3,1]再进行下一步,但这样操作很花时间,所以考虑不改变 a a a数组的顺序,因为求的是区间最值,所以如果这个数已经匹配好了,那么就把这个数删去,进行下一操作时在询问区间 [ 1 , a n s ] [1,ans] [1,ans]最值就好了,y因为询问 i i i,前面删了 i − 1 i-1 i1个数,所以等价于上面的 [ i , a n s ] [i,ans] [i,ans]
最后区间最小值用线段树或者树状数组维护一下就好了。

#include<bits/stdc++.h>

#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair<int, int> pii;

const int N=3e5+10;
const int MAXN=20010;
const LL MAX=2e18;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const LL mod=1e9+7;
int n,m,x,y,t,k,p,ans;

int a[N], h[N],b[N];

int lowbit(int x)
{
	return x & (-x);
}
void updata(int x)
{
	int lx, i;
	while (x <= n)
	{
		h[x] = a[x];
		lx = lowbit(x);
		for (i=1; i<lx; i<<=1)
			h[x] = min(h[x], h[x-i]);
		x += lowbit(x);
	}
}
int query(int x, int y)
{
	int ans = INF;
	while (y >= x)
	{
		ans = min(a[y], ans);
		y --;
		for (; y-lowbit(y) >= x; y -= lowbit(y))
			ans = min(h[y], ans);
	}
	return ans;
}

queue<int>id[N];

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            while(!id[i].empty()) id[i].pop();
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            id[a[i]].push(i);
        }
        for(int i=1;i<=n;i++) updata(i);
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
            if(id[b[i]].empty()) flag=1;
            if(flag!=1)
            {
                int sx=id[b[i]].front();
                id[b[i]].pop();
                if(query(1,sx)<b[i]) flag=1;
                else
                {
                    a[sx]=n+1;
                    updata(sx);
                }
            }
            /*for(int i=1;i<=n;i++)
                printf("%d ",h[i]);
            printf("\n");*/
        }
        flag?printf("NO\n"):printf("YES\n");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值