EDU 144 && EDU 143

A. Typical Interview Problem(思维)

题意:

给定一个由字符"F"和/或"B"组成的字符串s。我们需要确定这个字符串是否是FB字符串的子串,即是否存在两个整数l和r(1≤l≤r),使得字符串flfl+1fl+2…fr正好等于s。
FB字符串的构造方式如下。初始时,它是一个空字符串。我们按照从1开始的升序遍历所有的正整数,并对每个整数执行以下操作:

如果当前整数能被3整除,则在FB字符串的末尾添加字符"F";

如果当前整数能被5整除,则在FB字符串的末尾添加字符"B"。
需要注意的是,如果一个整数既能被3整除又能被5整除,则先添加字符"F",然后添加字符"B",而不是反过来。
FB字符串是无限长的。它的前10个字符是FBFFBFFBFB:第一个"F"来自整数3,下一个字符"B"来自整数5,下一个"F"来自整数6,依此类推。很容易看出,这个字符串是无限长的。
现在,我们需要判断给定的字符串s是否是FB字符串的子串。也就是说,我们需要确定是否存在两个整数l和r,使得字符串flfl+1fl+2…fr恰好等于s。
例如:

字符串FFB是FB字符串的子串:如果我们选择l=3和r=5,那么字符串f3f4f5恰好等于FFB;

字符串BFFBFFBF是FB字符串的子串:如果我们选择l=2和r=9,那么字符串f2f3f4…f9恰好等于BFFBFFBF;

字符串BBB不是FB字符串的子串。

思路:

前五分钟没想出什么思路,列一下就懂了,出现的数字分别是3 5 9 15 18 20
后面是20+3 20+5 20+9 20+15 20+18 20+20,循环了
所以直接找有无重复,无重复就是没有

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string cmp;
    for(int i=1;i<=500;i++)
    {
        if(i%3==0)cmp+='F';
        if(i%5==0)cmp+='B';
    }
    //cout<<cmp<<endl;
    int t;
    cin>>t;
    while(t--)
    {
        string kk,num;
        cin>>num>>kk;
        int len = kk.length();
        if(cmp.find(kk)!=-1)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;    
}

B. Asterisk-Minor Template(思维)

题意:

给定两个字符串a和b,它们由小写拉丁字母组成。
模板t是一个由小写拉丁字母和星号(字符’*')组成的字符串。如果模板中的星号数量小于或等于字母数量,那么这个模板被称为星号次要模板。
如果你可以用小写拉丁字母(可能为空字符串)替换模板t中的每个星号,使得它变成与字符串s相等,则称字符串s与模板t匹配。
找到一个星号次要模板,使得字符串a和b都与它匹配,或者报告这样的模板不存在。如果有多个答案,则可以任选一个打印出来

思路:

如果匹配,那么*的长度最大为2即可。
判断开头结尾是否相同
判断是否有连续的字母相连即可

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        string a;
        string b;
        cin>>a>>b;
        int lena = a.length(),lenb = b.length();
        if(a[0]==b[0])
        {
            cout<<"YES"<<endl;
            cout<<a[0]<<"*"<<endl;
            continue;
        }
        if(a[lena-1]==b[lenb-1])
        {
            cout<<"YES"<<endl;
            cout<<"*"<<a[lena-1]<<endl;
            continue;
        }
        int fl = 1;
        for(int i=0;i<lena-1;i++)
        {
            string cmp;
            cmp+=a[i];
            cmp+=a[i+1];
            if(b.find(cmp)!=-1)
            {
                cout<<"YES"<<endl;
                cout<<"*"<<cmp<<"*"<<endl;
                fl = 0;
                break;
            }
        }
        if(fl)
            cout<<"NO"<<endl;
 
    }
 
    return 0;
}

C. Maximum Set (简单数论)

题意:

给定两个整数 l 和 r,定义一个正整数集合 S 为美丽的,如果对于集合中的任意两个整数 x 和 y,x 整除 y 或者 y 整除
x(或者两者都满足)。 你需要考虑所有由不小于 l 且不大于 r 的整数组成的美丽集合。你需要打印两个数: 在所有元素都来自于 l 到 r
的美丽集合中,最大可能的美丽集合的大小; 由 l 到 r 的整数组成的美丽集合中,具有最大可能大小的美丽集合的数量。
由于第二个数可能非常大,你需要对其进行模运算,模数为 998244353。

思路:

若使得距离最长,那么就要求每次前进最短,那么我们每次乘以2就好了…
我只想到这一层,后面看了题解
发现在一堆乘以2的数字中,比如序列:
4 * 2 ,4 * 2 * 2, 4 * 2 * 2 * 2 , 4* 2 *2 * 2 *2…
可以变为
4 * 2,4 * 3 * 2 , 4 * 3 * 2 * 2…这样会使得长度相同,但结尾变大了,使得整个串也不相同了
我们就需要二分这个3最大可以出现在哪个地方即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
long long q_pow(long long a, long long n)
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return q_pow(a, n - 1) * a;
    else
    {
        int temp = q_pow(a, n / 2);
        return temp * temp;
    }
}
//(ans+mod)%mod可以防止负数
int main()
{
    long long t;
    cin>>t;
    while(t--)
    {
        long long ans = 0;
        long long l,r;
        scanf("%lld%lld",&l,&r);
        long long begin = l;
        long long len = 0;
        while(begin<=r)
        {
             len++;
             begin*=2;
        }
        cout<<len<<' ';
        //找2,找3
        long long ll = l-1 ,rr = r+1;
        while(ll<rr)
        {
            long long mid = (ll+rr+1)/2;
            //cout<<"mid"<<mid<<endl;
            //cout<<mid*q_pow(2,len-1)<<endl;
            if(mid*q_pow(2,len-1)<=r)//找到最大的且小与r的数
            {
                ll = mid;
            }
            else
            {
                rr = mid-1;
            }
        }
        //cout<<ll<<endl;
        if(ll>=l)
        ans+=ll-l+1;
        ll = l-1;rr = r+1;
        while(ll<rr)
        {
            long long mid = (ll+rr+1)/2;
            //cout<<"mid"<<mid<<endl;
            //cout<<mid*q_pow(2,len-1)<<endl;
            if(mid*q_pow(2,len-2)*3<=r)//找到最大的且小与r的数
            {
                ll = mid;
            }
            else
            {
                rr = mid - 1;
            }
        }
        //cout<<ll<<endl;
        if(ll!=l-1)
            ans += (ll - l + 1) * (len - 1);
        cout<<ans<<endl;
        //cout<<ll<<"?"<<endl;
 
    }
}

D. Maximum Subarray (DP)

题意:

给定一个由 n 个整数组成的数组 a1,a2,…,an,以及两个整数 k 和 x。 你需要执行以下操作:将 x 添加到恰好 k
个不同位置的元素上,并从其它元素中减去 x。每个元素只能选择一次。 例如,如果
a=[2,-1,2,3],k=1,x=2,并且我们选择了第一个元素,则操作后的数组为 a=[4,-3,0,1]。 定义 f(a) 为数组 a
的子数组的最大可能和。子数组是数组 a 的连续部分,即数组 ai,ai+1,…,aj,其中
1≤i≤j≤n。空的子数组应该被考虑在内,其和为 0。 令数组 a’ 是应用上述操作后的数组 a。以使 f(a’)
的值最大化的方式应用操作,并打印 f(a’) 的最大可能值。

思路:

没有想到要差分,想思路的过程太折磨了。。。。
当x大于0的时候,我们只需要找出区间长度为k的最大前缀和+k*x即可。
当x小于0的时候,这时候相当于选择n-k个点去加上x,找出长度为n-k的最大前缀和。
当k为0的时候,需要找的是长度为1到长度为len的最大前缀和,但当n=1e5的时候,这个复杂度太高了…想不出来其他方案,随后看题解
结果是dp…
f[i][j] 表示的是按照顺序选到第i个数字的时候,增加了j个的连续区间的最大值。
所以有
1.当i==j的时候,前面全部都加了,自己必须也加x。
2.定义合法范围。

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

typedef long long LL;

const int N = 2e5 + 10, M = 30;

int a[N];
LL f[N][M];

void solve() {
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            f[i][j] = -0x3f3f3f3f3f3f3f3f;
        }
    }
    f[0][0] = 0;
    LL ret = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = max(0, m - n + i); j <= m && j <= i; j++) {
            if (j < i) f[i][j] = max(0ll, f[i - 1][j]) + a[i] - k;
            if (j) f[i][j] = max(f[i][j], max(0ll, f[i - 1][j - 1]) + a[i] + k);
            ret = max(ret, f[i][j]);
        }
    }
    printf("%lld\n", ret);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }

    return 0;
}

A. Two Towers (签到题)

题意:

有两座由红色和蓝色方块组成的塔楼。这两座塔楼由字符B和/或R表示,表示从底部到顶部的方块顺序,其中B表示蓝色方块,R表示红色方块。
这两座塔楼由字符串BRBB和RBR表示。 你可以执行以下操作任意次数:选择一座至少有两个方块的塔楼,将其顶部方块移到另一座塔楼的顶部。
如果没有相邻的方块颜色相同(即没有红色方块在另一个红色方块上方,也没有蓝色方块在另一个蓝色方块上方),则这对塔楼是美丽的。
你需要判断是否可以执行任意次数的操作(可能为零次),使得这对塔楼变得美丽。

思路:

模拟了一下过程,发现拼接是有顺序的,要么a拼接翻转b,要么b拼接翻转a
拼接完毕后设断点,如果有两个连续的BB或者RR,断点放在任何一个地方都不行。
如果有3个或者3个以上的的连续字串,怎么样都不可以

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        string a;
        string b;
        string ra;
        string rb;
        int n1,n2;
        cin>>n1>>n2>>a>>b;
        int lena = a.length();
        int lenb = b.length();
        for(int i=lena-1;i>=0;i--)
        {
            ra+=a[i];
        }
        for(int i=lenb-1;i>=0;i--)
        {
            rb+=b[i];
        }
        string des1;
        string des2;
        des1 = a+rb;
        des2 = b+ra;
        int sum = 0;
        int len1 = des1.length();
        int len2 = des2.length();
        int fl = 0;
        for(int i=0;i<len1;i++)
        {
             //连续字串
             int len = 1;
             while(des1[i]==des1[i+1])
             {
                i++;
                len++;
             }
             if(len==2)
             {
                sum++;
             }
             if(len>=3)
             {
                cout<<"NO"<<endl;
                fl = 1;
                break;
             }
             //cout<<len<<' ';
        }
        if(fl)continue;
        //<<des1<<endl;
        if(sum<=1)
        {
            cout<<"YES"<<endl;
            continue;
        }
        sum = 0;
        for(int i=0;i<len2;i++)
        {
             //连续字串
             int len = 1;
             while(des2[i]==des2[i+1])
             {
                i++;
                len++;
             }
             if(len>=2)
             {
                sum++;
             }
             if(len>2)
             {
                fl = 1;
                cout<<"NO"<<endl;
             }
        }
        if(fl)continue;
        if(sum<=1)
        {
            cout<<"YES"<<endl;
            continue;
        }
        cout<<"NO"<<endl;

    }
    return 0;
}

B . Ideal Point(差分)

题意:

你被给定一个由n个整数组成的数组a1,a2,…,an。同时给定两个整数k和x。
你需要执行以下操作:恰好一次地,在k个不同的位置上加上x,并从其他所有元素中减去x。
例如,如果a=[2,-1,2,3],k=1,x=2,并且我们选择了第一个元素进行操作,那么操作后的数组a=[4,-3,0,1]。
定义f(a)为数组a的子数组的最大可能和。子数组是指数组a的连续一部分,即对于某些1≤i≤j≤n,数组ai,ai+1,…,aj。空子数组也应该被考虑在内,它的和为0。
令数组a’为应用上述操作后的数组a。请以使f(a’)的值最大化的方式执行操作,并输出f(a’)的最大可能值。

思路:

想了一下可能性,当所有线段都关于他的时候,他是最大的,或者只有和他相等的,这时无论减少多少条线段,他都会减一,但是可能和他相等的数不会减一。所以不如我们直接只选择这个x所在的区间,进行差分,最终看结果是否只有他最大,如果有和他相等的,输出NO

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5;
int cf[N];
int a[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;
        int maxr = 0;
        for(int i=1;i<=n;i++)
        {
            int l,r;
            cin>>l>>r;
            if(k>=l && k<=r)
            {
                cf[l]+=1;
                cf[r+1]-=1;
                maxr = max(maxr,r);
            }
            
        }
        int rk = 0;
        if(maxr==0)
        {
            cout<<"NO"<<endl;
            continue;
        }   
        //cout<<maxr<<endl;
        for(int i=1;i<=maxr;i++)
        {
            rk += cf[i];
            a[i] = rk;
        }
        //cout<<endl;
        int des = a[k];
        int fl = 1;
        //cout<<des<<endl;
        for(int i=1;i<=maxr;i++)
        {
            if(i==k)continue;
            if(a[i]==des)
            {
                cout<<"NO"<<endl;
                fl = 0;
                break;
            }
        }
        if(fl)cout<<"YES"<<endl;
        memset(cf,0,sizeof cf);
    }
    return 0;
}

C. Tea Tasting (二分 前缀和 差分)

题意:

一个茶叶生产商决定进行一次大规模的品茶活动。n种茶叶将由n个品茶师品尝。茶叶和品茶师都从1到n进行编号。生产商准备了ai毫升的第i种茶叶。第j个品茶师一次可以喝bj毫升的茶。
品茶活动将分步进行。在第一步中,第i个品茶师品尝第i种茶叶。第i个品茶师喝下min(ai,bi)毫升的茶(取决于第i种茶叶的剩余量和第i个品茶师一次可以喝的量)。ai也会相应减少这个数量。
然后所有的品茶师都移动到上一种茶叶。因此,在第二步中,第i个品茶师品尝第(i-1)种茶叶。第i个品茶师喝下min(ai-1,bi)毫升的茶。第1个人结束品茶活动。
在第三步中,第i个品茶师品尝第(i-2)种茶叶。第2个品茶师结束品茶活动。这一过程一直持续到所有人结束品茶活动。
以n=3,a=[10,20,15],b=[9,8,6]为例,左列显示了每种茶叶的当前数量,右列显示了每个品茶师总共喝了多少茶。箭头表示茶叶在当前步骤中分给哪个品茶师。箭头上的数字是茶叶的数量,取决于茶叶的剩余量和品茶师一次可以喝的量的最小值。

思路:

想到差分 想到前缀和 想到第i个茶哥只能喝到i到n杯茶。又想到在不限制茶叶的情况下,茶叶会被消耗多少,茶叶量可以透支,变为负数,当他变为负数的时候,我们只需要让喝茶兄弟背债即可,后面把他所喝的茶叶减去所需还债量就是他喝的多少,但是复杂度很高,不通过。
看了题解:可以这样想,每个茶叶最多供应多少杯茶叶,当到第i个人品茶的时候,就不能取了。用前缀和,二分实现。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
 
using namespace std;
 
const int N = 2e5 + 10;
 
int T;
int a[N], b[N], cf[N], add[N], pre[N];
 
int find(int x, int tmp, int nn)
{
	int l = tmp + 1, r = nn;
	while(l < r)
	{
		int mid = l + r + 1>> 1;
		if(pre[mid] - pre[tmp] <= x) l = mid;
		else r = mid - 1;
	}
	return r;
}
 
signed main()
{
	scanf("%lld", &T);
	int n;
	while(T--)
	{
		scanf("%lld", &n);
		for (int i = 1; i <= n; ++ i) 
		{
		    scanf("%lld", &a[i]);
		    pre[i] = 0, cf[i] = 0, add[i] = 0;
		}
		for (int i = 1; i <= n; ++ i) scanf("%lld", &b[i]), pre[i] = pre[i - 1] + b[i]; 
		int k = 0;
		add[n] = min(b[n], a[n]);
		for (int i = 1; i < n; ++ i)
		{
			int ci = find(a[i], k, n);
			if(b[i] > a[i]) {
			    add[i] += a[i];
			    k++;
			    continue;
			}
			int now = ci - k;
			k ++;
			cf[k] ++, cf[k + now] --;
			if(pre[ci] - pre[k - 1] < a[i]) add[k + now] += a[i] - pre[ci] + pre[k - 1];
		} 
		for (int i = 1; i <= n; ++ i)
		{
		    cf[i] += cf[i - 1];
		    printf("%lld ", cf[i] * b[i] + add[i]);
		}
		puts("");
	}
	return 0;
}

D Triangle Coloring(数论)

题意:

给定一个由n个顶点和n条边组成的无向图,其中n是6的倍数。每条边的权重是一个正整数。
图的结构如下:它被分为n/3个顶点三元组,第一个三元组由顶点1、2、3组成,第二个三元组由顶点4、5、6组成,依此类推。同一个三元组中的任意两个顶点之间都有一条边相连,不同三元组之间没有边相连。
你需要将图的顶点涂成两种颜色,红色和蓝色。每个顶点必须有且只有一种颜色,其中红色顶点的数量应为n/2,蓝色顶点的数量也应为n/2。满足这些约束条件的着色被称为有效着色。
着色的权重是连接两个不同颜色顶点的边的权重之和。
设W为有效着色的最大可能权重。计算具有权重W的有效着色的数量,并以模998244353的形式打印出来。

思路:

没看懂题目一开始,以为是图论,后面慢慢读就懂了。。。
题解:
设当前遍历的区间中的三个数为a b c
1.abc 是一样的:那么随便染色,三种情况
2.存在ab>c 那么有两种不同的染色选择
3.存在b
c<a,那么有两种不同染色选择
4.存在三个不一样的,那么只有一两种情况

因为存在p为质数,m和n过大,所以可以用卢卡斯定理算组合数
用乘法原理+卢卡斯定理(内容来自B站董晓算法)
在这里插入图片是描述

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

typedef long long ll;
const int mod =  998244353;
const int N = 3e5 + 10;

ll qmi(ll a,ll b,ll p)
{
    ll res = 1;
    while(b)
    {
        if(b & 1)res = res % p * a % p;
        a = a % p * a % p;
        b >>= 1;
    }
    return res;
}//快速幂
ll c(ll a,ll b, ll p)//卢卡斯定理求组合数
{
    if(b > a)return 0;
    ll res = 1;
    for(int i = 1,j = a;i <= b; i++,j--)
    {
        res = res % p * j % p;
        res = res % p * qmi(i, p-2, p) % p;
    }
    return res;
}//卢卡斯
ll lucas(ll a,ll b,ll p)
{
    if(a < p && b < p)return c(a,b,p);
    else return c( a % p,b % p, p) * lucas(a / p, b / p, p ) % p;
}
int v[N];
void solve()
{
	int n;
	cin >> n;
	int cnt = 0;
	ll res = 1;
    int a,b,c;
	for(int i = 0; i < n/3; i ++ )
	{
		cin>>a>>b>>c;
		cnt = 0;
		int maxm = 0;
		maxm = max(a, max(b, c));
		if(a != b && b != c && c != a)//全都不相等,只有一种可能
			res *= 1;
		else if( a == b && b == c && a == c)//三个相等
			res = (res * 3) % mod;
		else
		{
			multiset<int> ma;
			ma.insert(a);
			ma.insert(b);
			ma.insert(c);
			if(ma.count(maxm) != 2)//两个大于一个 就乘以2,否则只有一种选择
				res = (res * 2) % mod;
		}
	}
		cout << ( res * lucas(n / 3, n / 6, mod) ) % mod << endl; // 总数量再乘以染色数(C(n/3,n/6));公有n/3组,从这些组选出一半染色
}
int main()
{
	solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值