Acm y总的每日一题
Acm y总的每日一题
2月17-2月23日
星期一
农夫约翰的奶酪块
农夫约翰有一块立方体形状的奶酪,它位于三维坐标空间中,从 (0,0,0)(0,0,0) 延伸至 (N,N,N)(N,N,N)。
农夫约翰将对他的奶酪块执行一系列 QQ 次更新操作。
对于每次更新操作,农夫约翰将从整数坐标 (x,y,z)(x,y,z) 到 (x+1,y+1,z+1)(x+1,y+1,z+1) 处切割出一个 1×1×11×1×1 的奶酪块,其中 0≤x,y,z<N0≤x,y,z<N。
输入保证在农夫约翰切割的位置上存在一个 1×1×11×1×1 的奶酪块。
由于农夫约翰正在玩牛的世界,当下方的奶酪被切割后,重力不会导致上方的奶酪掉落。
在每次更新后,输出农夫约翰可以将一个 1×1×N1×1×N 的砖块插入奶酪块中的方案数,使得砖块的任何部分都不与剩余的奶酪重叠。
砖块的每个顶点在全部三个坐标轴上均必须具有整数坐标,范围为 [0,N][0,N]。
农夫约翰可以随意旋转砖块。
输入格式
输入的第一行包含 NN 和 QQ。
以下 QQ 行包含 xx,yy 和 zz,为要切割的位置的坐标。
输出格式
在每次更新操作后,输出一个整数,为所求的方案数。
数据范围
2≤N≤10002≤N≤1000,
1≤Q≤2×1051≤Q≤2×105,
0≤x,y,z<N0≤x,y,z<N
输入样例:
2 5
0 0 0
1 1 1
0 1 0
1 0 0
1 1 0
输出样例:
0
0
1
2
5
题解:
将三维坐标转化为二维坐标,a[x][y],b[y][z],c[x][z],当达到》n时候++
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
const int N=1010,M=1e3+10,mod=1e9+7;
int a[N][N],b[N][N],c[N][N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,q;
cin>>n>>q;
int ans=0;
while(q--){
int x,y,z;
cin>>x>>y>>z;
a[x][y]++;
b[y][z]++;
c[x][z]++;
if(a[x][y]>=n)ans++;
if(b[y][z]>=n)ans++;
if(c[x][z]>=n)ans++;
cout<<ans<<endl;
}
return 0;
}
星期二
哞叫时间
农夫约翰正在试图向埃尔茜描述他最喜欢的 USACO 竞赛,但她很难理解为什么他这么喜欢它。
他说「竞赛中我最喜欢的部分是贝茜说 『现在是哞哞时间』并在整个竞赛中一直哞哞叫」。
埃尔茜仍然不理解,所以农夫约翰将竞赛以文本文件形式下载,并试图解释他的意思。
竞赛被定义为一个长度为 NN 的小写字母字符串。
一种哞叫一般地定义为子串 cicjcjcicjcj,其中某字符 cici 之后紧跟着 22 个某字符 cjcj,且 ci≠cjci≠cj。
根据农夫约翰的说法,贝茜哞叫了很多,所以如果某种哞叫在竞赛中出现了至少 FF 次,那可能就是贝茜发出的。
然而,农夫约翰的下载可能损坏,文本文件可能存在至多一个字符与原始文件不同。
将可能的误差考虑在内,输出所有可能是贝茜发出的哞叫,按字母顺序排序。
输入格式
输入的第一行包含 NN 和 FF,表示字符串的长度以及贝茜的哞叫的频次下限。
第二行包含一个长度为 NN 的小写字母字符串,表示竞赛。
输出格式
输出可能是贝茜发出的哞叫的数量,以下是按字典序排序的哞叫列表。
每行输出一种哞叫。
数据范围
3≤N≤200003≤N≤20000,
1≤F≤N1≤F≤N
输入样例1:
10 2
zzmoozzmoo
输出样例1:
1
moo
样例1解释
在这个样例中,任何字符变化都不会影响答案。
唯一贝茜可能发出的哞叫是 moo。
输入样例2:
17 2
momoobaaaaaqqqcqq
输出样例2:
3
aqq
baa
cqq
题意:给定字符串找到abb的个数,个数大于n个为有效个数,我们可以对字符串中的数组的一个字符进行修改,求这样的方案数;
题解:
先遍历一遍,数组找出现有的个数,然后再去改变没有位可能,改变只会影响,相近的三位数,那么我们就可以用一个updat函数,
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
const int N=2e5+10,M=26,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
char s[N];
int n,f=0;
int cnt[M][M];
bool st[M][M];
void update(int l,int r,int v){//统计l到r中的有满足条件
l=max(l,0ll);
r=min(r,n-1);
for(int i=l;i+2<=r;i++){
char a=s[i],b=s[i+1],c=s[i+2];
if(a!=b&&b==c){
cnt[a][b]+=v;
if(cnt[a][b]>=f){
st[a][b]= true;
}
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
scanf("%lld%lld%s",&n,&f,s);
for(int i=0;i<n;i++)s[i]-='a';
update(0,n-1,1);
REP(i,n){
char t=s[i];
update(i-2,i+2,-1);//先减去这三个影响
REP(j,26){
if(j!=t){
s[i]=j;
update(i-2,i+2,1);//修改之后的影响,
update(i-2,i+2,-1);//回归修改之后的状态。
}
}
s[i]=t;
update(i-2,i+2,1);//还原回来。
}
int ans=0;
REP(i,26){
REP(j,26){
if(st[i][j]){
ans++;
}
}
}
printf("%lld\n",ans);
REP(i,26){
REP(j,26){
if(st[i][j]){
printf("%c%c%c\n",i+'a',j+'a',j+'a');
}
}
}
return 0;
}
星期三
蛋糕游戏
贝茜和埃尔茜发现了一行 NN 个蛋糕(NN 为偶数),大小依次为 a1,a2,…,aNa1,a2,…,aN。
两头奶牛都想吃到尽可能多的蛋糕。
但是,作为非常文明的奶牛,她们决定玩一个游戏来分割蛋糕!
游戏在两头奶牛之间轮流进行回合。
每个回合进行以下两者之一:
- 贝茜选择两个相邻的蛋糕并将它们堆叠起来,制造大小为两者大小之和的一个新蛋糕。
- 埃尔茜选择最左边或最右边的蛋糕藏起来。
当只剩下一个蛋糕时,贝茜吃掉它,而埃尔茜吃掉她藏起来的所有蛋糕。
如果两头奶牛都采取最优策略以最大化她们吃到的蛋糕量,并且贝茜先进行回合,那么每头奶牛将会吃到多少蛋糕?
输入格式
每个测试点包含 TT 个独立的测试用例。
每个测试用例的格式如下。
第一行包含 NN。
下一行包含 NN 个空格分隔的整数 a1,a2,…,aNa1,a2,…,aN。
输出格式
对于每个测试用例,输出一行,包含 bb 和 ee,表示贝茜和埃尔茜在两头奶牛都采取最优策略的情况下分别吃到的蛋糕量。
数据范围
1≤T≤101≤T≤10,
2≤N≤5×1052≤N≤5×105,
1≤ai≤1091≤ai≤109,
输入保证一个测试点中的所有 NN 之和不超过 106106。
输入样例:
2
4
40 30 20 10
4
10 20 30 40
输出样例:
60 40
60 40
题意:
本题要求解一个最优策略,举几个例子可以发现,每一步贝茜都要保证自己所拿的蛋糕不偏向两边,否则就会被埃尔茜拿走,因此两人的策略为
题解:
贪心,前缀和
我们只要管拿两边的这个人拿的最大值就可以,因为确定了他拿的个数,所以计算前缀和,后缀和,然后计算最大值,两边的最大值。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=5e5+10,M=1e3+10,mod=1e9+7;
int a[N],sum1[N],sum2[N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
TESTS{
int n;
cin>>n;
int k=n/2+1;
int l=n-k;
//左方向前缀和
for(int i=1;i<=n;i++){
cin>>a[i];
sum1[i]=sum1[i-1]+a[i];
}
//右方向前缀和
for(int i=1;i<n;i++){
int j=n-i+1;
sum2[i]=sum2[i-1]+a[j];
}
//开始选择左右边最大的数;
int maxx=0;
for(int i=0;i<=l;i++){
int j=l-i;
maxx=max(sum1[i]+sum2[j],maxx);
}
cout<<sum1[n]-maxx<<" "<<maxx<<endl;
};
return 0;
}
星期四
哞叫时间II
农夫约翰正在试图向埃尔茜描述他最喜欢的 USACO 竞赛,但她很难理解为什么他这么喜欢它。
他说「竞赛中我最喜欢的部分是贝茜说『现在是哞哞时间』并在整个竞赛中一直哞哞叫」。
埃尔茜仍然不理解,所以农夫约翰将竞赛以文本文件形式下载,并试图解释他的意思。
竞赛被定义为一个包含 NN 个整数的数组 a1,a2,…,aNa1,a2,…,aN。
农夫约翰定义哞叫为一个包含三个整数的数组,其中第二个整数等于第三个整数,但不等于第一个整数。
一种哞叫被称为在竞赛中发生,如果可以从数组中移除整数,直到只剩下这一哞叫。
由于贝茜据称「在整个竞赛中一直哞哞叫」,请帮助埃尔茜计算竞赛中发生的不同哞叫的数量!
两种哞叫是不同的,如果它们并非由相同的整数以相同的顺序组成。
输入格式
输入的第一行包含 NN。
第二行包含 NN 个空格分隔的整数 a1,a2,…,aNa1,a2,…,aN。
输出格式
输出竞赛中发生的不同哞叫的数量。
注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,Java 中的 “long”,C/C++ 中的 “long long”)。
数据范围
1≤N≤1061≤N≤106,
1≤ai≤N1≤ai≤N
输入样例:
6
1 2 3 4 4 4
输出样例:
3
题意:
题目大致意思为保持原数组相对顺序不变,从中提取任意位置的三个数字,组成abb形式的个数。我们只考虑abb中倒数第二个b的出现位置,此时加上b前面的除b之外的数字种类个数就是所有形如xbb的形式个数,因此可以把每个数字出现的次数先预处理出来,只有当数字个数>=2时,才有可能出现xbb,此时若是倒数第二个b,那就把前面的数字种类累加,最后就是答案
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
vector<int>v(n);
REP(i,n)cin>>v[i];
int ans=0;
vector<int>count(n+1,0);
vector<int>cnt(n+1,0);
vector<bool>st(n+1,false);
int type = 0;
REP(i,n){
count[v[i]]++;
}
REP(i,n){
if(!st[v[i]]){
st[v[i]]=true;
type++;//记录连续子区间前面不重复的数字个数
}
cnt[v[i]]++;
if(count[v[i]]>=2&&cnt[v[i]]+1==count[v[i]])//判断是不是倒数的数字abb
{
ans+=type-1;
}
}
cout<<ans<<endl;
return 0;
}
星期五
奶牛体检
农夫约翰的 NN 头奶牛站成一行,奶牛 11 在队伍的最前面,奶牛 NN 在队伍的最后面。
农夫约翰的奶牛也有许多不同的品种。
他用从 11 到 NN 的整数来表示每一品种。
队伍从前到后第 ii 头奶牛的品种是 aiai。
农夫约翰正在带他的奶牛们去当地的奶牛医院进行体检。
然而,奶牛兽医非常挑剔,仅愿意当队伍中第 ii 头奶牛为品种 bibi 时对其进行体检。
农夫约翰很懒惰,不想完全重新排列他的奶牛。
他将执行以下操作恰好一次。
- 选择两个整数 ll 和 rr,使得 1≤l≤r≤N1≤l≤r≤N。反转队伍中第 ll 头奶牛到第 rr 头奶牛之间的奶牛的顺序。
农夫约翰想要衡量这种方法有多少效果。
对于每一个 c=0…Nc=0…N,请帮助农夫约翰求出使得恰好 cc 头奶牛被检查的不同操作 (l,r)(l,r) 的数量。
两种操作 (l1,r1)(l1,r1) 和 (l2,r2)(l2,r2) 不同,如果 l1≠l2l1≠l2 或者 r1≠r2r1≠r2。
输入格式
输入的第一行包含 NN。
第二行包含 a1,a2,…,aNa1,a2,…,aN。
第三行包含 b1,b2,…,bNb1,b2,…,bN。
输出格式
输出 N+1N+1 行,第 ii 行包含使得 i−1i−1 头奶牛被检查的不同操作 (l,r)(l,r) 的数量。
数据范围
1≤N≤75001≤N≤7500,
1≤ai,bi≤N1≤ai,bi≤N
输入样例1:
3
1 3 2
3 2 1
输出样例1:
3
3
0
0
样例1解释
如果农夫约翰选择 (l=1,r=1)(l=1,r=1),(l=2,r=2)(l=2,r=2) 或 (l=3,r=3)(l=3,r=3),则没有奶牛将会被检查。
注意这些操作并没有改变奶牛的位置。
以下操作会导致一头奶牛被检查。
- (l=1,r=2)(l=1,r=2):农夫约翰反转第一头和第二头奶牛的顺序,因此新队伍中每头奶牛的品种将为 [3,1,2][3,1,2]。第一头奶牛将会被检查。
- (l=2,r=3)(l=2,r=3):农夫约翰反转第二头和第三头奶牛的顺序,因此新队伍中每头奶牛的品种将为 [1,2,3][1,2,3]。第二头奶牛将会被检查。
- (l=1,r=3)(l=1,r=3):农夫约翰反转第一头,第二头和第三头奶牛的顺序,因此新队伍中每头奶牛的品种将为 [2,3,1][2,3,1]。第三头奶牛将会被检查。
输入样例2:
3
1 2 3
1 2 3
输出样例2:
0
3
0
3
样例2解释
三种导致 33 头奶牛被检查的可能操作为 (l=1,r=1)(l=1,r=1),(l=2,r=2)(l=2,r=2) 和 (l=3,r=3)(l=3,r=3)。
输入样例3:
7
1 3 2 2 1 3 2
3 2 2 1 2 3 1
输出样例3:
0
6
14
6
2
0
0
0
样例3解释
两种导致 44 头奶牛被检查的可能操作为 (l=4,r=5)(l=4,r=5) 和 (l=5,r=7)(l=5,r=7)。
题意:
将换后可以有多少个点重合
解法1,暴力TLE(6/13)
我们定义“匹配度”为:a串与b串相同位置相同字符的数量。
暴力做法枚举左端点和右端点,然后每次check区间[l,r]翻转后的匹配度;
具体check函数就是重新统计区间:
循环l<r, l++ r--然后依次计算翻转当前a[l],a[r]改变了多少贡献度
#include <bits/stdc++.h>
using namespace std;
const int N = 7510;
int a[N],b[N];
int ans[N];
int n;
int cnt;
void check(int l, int r){
int t = cnt;
for(l,r;l<r;l++,r--){
if(a[l] == b[l])t--;
if(a[r] == b[r])t--;
if(a[l] == b[r])t++;
if(a[r] == b[l])t++;
}
ans[t]++;
}
int main(){
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
for(int i = 0; i < n; i++){
cin >> b[i];
if(b[i] == a[i])cnt++;
}
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
check(i,j);
}
}
for(int i = 0; i <= n; i++){
cout << ans[i] << endl;
}
return 0;
}
优化 重复计算优化
我们会发现,当我们在计算长度大于3的翻转的时候
如:[l,r],其会等同于[l+1,r-1]翻转,再加上a[l]和a[r]翻转
所以我们改枚举l,r为枚举长度和右端点
w[l][r] = w[l+1][r-1] + swap(l,r);
//细节1:注意1和2的时候单独判别即可
//细节2:7500*7500数组大概要用220m,题目给的256够用,如果用hash(map)也会超时
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=8000,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
int a[N],b[N];
int w[N][N];
int n;
int ans[N];
int cnt;
int swap(int x,int y){
int t=0;
if(a[x]==b[x])t--;
if(a[y]==b[y])t--;
if(a[x]==b[y])t++;
if(a[y]==b[x])t++;
return t;
}
void check(int l,int r){
if(r-l>1){
w[l][r]=w[l+1][r-1]+swap(l,r);
}else{
w[l][r]=cnt+swap(l,r);
}
ans[w[l][r]]++;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
if(a[i]==b[i])cnt++;
}
for(int len=1;len<=n;len++){
for(int r=len;r<=n;r++){
int l=r-len+1;
check(l,r);
}
}
for(int i=0;i<=n;i++){
cout<<ans[i]<<endl;
}
return 0;
}
2月24-3月2号
星期一
农夫约翰最喜欢的操作
又是农夫约翰的农场上寒冷而无聊的一天。
为了打发时间,农夫约翰发明了一种关于在整数数组上进行操作的有趣的休闲活动。
农夫约翰有一个包含 NN 个非负整数的数组 aa 和一个整数 MM。
然后,农夫约翰会请贝茜给出一个整数 xx。
在一次操作中,农夫约翰可以选择一个索引 ii,并对 aiai 加 11 或减 11。
农夫约翰的无聊值是他必须执行的最小操作次数,以使得对于所有的 1≤i≤N1≤i≤N,ai−xai−x 均可被 MM 整除。
对于所有可能的 xx,输出农夫约翰的最小无聊值。
输入格式
输入的第一行包含 TT,为需要求解的独立的测试用例数量。
每一个测试用例的第一行包含 NN 和 MM。
第二行包含 a1,a2,…,aNa1,a2,…,aN。
输出格式
对于每一个测试用例输出一行,包含对于所有可能的 xx,农夫约翰的最小无聊值。
数据范围
1≤T≤101≤T≤10,
1≤N≤2×1051≤N≤2×105,
1≤M≤1091≤M≤109,
0≤ai≤1090≤ai≤109,
输入保证所有测试用例的 NN 之和不超过 5×1055×105。
输入样例:
2
5 9
15 12 18 3 8
3 69
1 988244353 998244853
输出样例:
10
21
样例解释
在第一个测试用例中,xx 的一个最优选择是 33。
农夫约翰可以执行 1010 次操作使得 a=[12,12,21,3,12]a=[12,12,21,3,12]。
题解:
前缀和+贪心
题目求解对于所有的ai-x要能被M整除,也就是同余于M,这里的X是一个任意值。
对于数组中所有的ai,要满足ai减去一个任意值x后与M取余后的结果相等,实际上这里的x对结果没有影响——对于相等的数减不减x取余的结果都是一样的;对于不等的数,减不减取余后二者的差值也一样
首先对数组中所有的数取余M,我们要做的就是通过加一减一操作让取余后的结果相等,对于[1,4,7]想让结果一样的最少操作(一次操作是加一减一)是将所有的数都变为中位数,即1->4,7->4,如果数组长度是偶数那么[1,2,3,4]那么就取2和3作为中位数的最小代价,该方法称为中位数贪心。
本题中由于是余数,对于
3 69
1 988244353 998244853
取余后有[1, 49, 64],但是1可以通过减小来缩短与中位数的距离也就是1->0->68,这样就不能使用中位数贪心了,不过可以通过转化即将数组扩充为[1, 49, 64, 70(1+69), 118(49+60), 133(64+69)]这样1通过减小到达中位数的情况转换为60通过减小到达中位数。
将数组转换为枚举其中长度为n的子数组对于[1, 49, 64, 70(1+69), 118(49+60), 133(64+69)] 就是[1,49,64],[49,64,70],[64,70,118]…
通过中位数贪心解得最小的操作次数,为了加快计算可以使用前缀和,假设有区间l,r中位数位置为idx,那么所需的操作次数(区间中所有值与中位数的绝对值之和)=[idx,r]区间和-中位数*[idx,r]区间的大小+中位数*[l,idx-1]区间的大小-[l,idx-1]的区间和
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 4e5 + 10;
int n, m;
ll a[N], s[N];
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
cin >> a[i], a[i] %= m;
for (int i = 1; i <= n; i ++) a[i + n] = a[i] + m;
sort(a + 1, a + 1 + 2 * n);
for (int i = 1; i <= 2 * n; i ++) s[i] = s[i - 1] + a[i];
ll res = 1e18;
for (int i = 1; i <= n; i ++) {
int l = i, r = i + n - 1;
int mid = (l + r) >> 1;
ll sum = (2 * mid - l - r) * a[mid] + s[l - 1] - s[mid - 1] + s[r] - s[mid];
res = min(res, sum);
}
cout << res << '\n';
}
int main()
{
int t;
cin >> t;
while (t --) solve();
return 0;
}
星期二
拐杖糖盛宴
农夫约翰的奶牛们非常爱吃甜食,尤其爱吃拐杖糖。
约翰一共有 NN 头奶牛,编号 1∼N1∼N,其中第 ii 头奶牛的初始高度为 aiai。
约翰给奶牛们准备了 MM 根拐杖糖,编号 1∼M1∼M,其中第 ii 根的高度为 bibi。
约翰会按照糖果的编号顺序,每次拿出一根糖果喂给奶牛们,直到所有糖果都被喂完为止。
每当拿出一根糖果后,约翰会将其上端固定悬挂,下端自由下垂至刚好接触地面。
然后,奶牛们按照编号顺序,依次走到糖果面前,将糖果自下而上的啃食至自己的高度(因为更高的地方吃不到了)。
由于糖果上端是固定的,所以即使奶牛吃掉糖果的下端部分,糖果也会悬挂在最初设置的位置,不会下降至地面。
当轮到一个奶牛时,如果糖果剩余部分的底部高度已经超过了该奶牛的高度,那么它将什么都吃不到。
在所有奶牛都轮过一次后,不论这根糖果是否被吃完,该糖果都会被约翰扔掉,并换上下一根糖果,继续下一轮次的吃糖(仍然从编号为 11 的奶牛开始)。
另外,每轮过后,糖果都有可能令奶牛们的身高有所增长,具体为一头奶牛在本轮次吃掉了多少长度的糖果,其身高就会增高多少长度。
请你计算,当所有糖果都喂食完毕后,每头奶牛的最终高度。
输入格式
第一行包含两个整数 N,MN,M。
第二行包含 NN 个整数 a1,a2,…,aNa1,a2,…,aN。
第三行包含 MM 个整数 b1,b2,…,bMb1,b2,…,bM。
输出格式
共 NN 行,其中第 ii 行输出第 ii 头奶牛的最终高度。
数据范围
1≤N,M≤2×1051≤N,M≤2×105,
1≤ai,bi≤1091≤ai,bi≤109
输入样例:
3 2
3 2 5
6 1
输出样例:
7
2
7
样例解释
第 11 根糖果的高度为 66:
- 第 11 头牛吃掉高度不超过 33 的部分,糖果剩余部分高度 [3,6][3,6]。
- 第 22 头牛不够高,吃不到任何糖果。
- 第 33 头牛吃掉高度不超过 55 的部分,糖果剩余部分高度 [5,6][5,6]。
第 11 根糖果喂完,奶牛们的高度变为 [3+3,2+0,5+2]=[6,2,7][3+3,2+0,5+2]=[6,2,7]。
第 22 根糖果的高度为 11,会被第 11 头奶牛全部吃掉。
所以,最终奶牛们的高度变为 [7,2,7][7,2,7]。
题解:
暴力模拟,记得开数组,开vector过不了。
如果数组大小是固定的,并且对性能要求极高(如竞赛编程中的某些场景),建议使用 int v[N]。
如果数组大小需要动态变化,或者需要使用标准库提供的丰富功能(如插入、删除等),建议使用 vector<int>。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
const int N=2e5+10,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
int v[N],h[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
// vector<int>v(n+1,0);
// vector<int>h(n+1,0);
REP(i,n)cin>>v[i];
REP(i,m)cin>>h[i];
for(int i=0;i<m;i++){
int t=0;
for(int j=0 ;j<n;j++){
if(t>=h[i])break;
if(v[j]>t){
int d=v[j];
v[j]+=min(h[i]-t,v[j]-t);
t=d;
}
}
}
REP(i,n){
cout<<v[i]<<endl;
}
return 0;
}
星期三
密接牛追踪2
农夫约翰有 NN 头奶牛排成一排,从左到右依次编号为 1∼N1∼N。
不幸的是,有一种传染病正在蔓延。
最开始时,只有一部分奶牛受到感染。
每经过一个晚上,受感染的牛就会将病毒传染给它左右两侧的牛(如果有的话)。
一旦奶牛被感染,它就会一直被感染,无法自愈。
给定一个经过若干个夜晚后的奶牛的整体状态,其中哪些奶牛已经被感染,哪些奶牛尚未被感染统统已知。
请你计算,最开始时就受到感染的奶牛的最小可能数量。
输入格式
第一行包含整数 NN。
第二行包含一个长度为 NN 的 0101 序列,用来表示给定的奶牛的整体状态,其中第 ii 个字符如果是 11 则表示第 ii 头奶牛已经被感染,如果是 00 则表示第 ii 头奶牛尚未被感染。
输出格式
一个整数,表示最开始时就受到感染的奶牛的最小可能数量。
数据范围
1≤N≤3×1051≤N≤3×105
输入样例1:
5
11111
输出样例1:
1
样例1解释
初始时,任意一头奶牛被感染,一定天数后都可以使得所有奶牛被感染。
输入样例2:
6
011101
输出样例2:
4
题解:
转化为区间覆盖问题。
牛覆盖这个连续的区间,左右两边的话要用2*r+1;那么九直接(ci/(2*r+1));
先用v数组存取最连续1的长度,再根据计算出最小的天数,r,只有当为最小天数时候才可以满足所有的文字。
这个得分两种情况,第一种再中间的时候,
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
const int N=2e5+10,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
vector<int>v;//存储每一个区间,的长度,
int n;
string s;
cin>>n;
cin>>s;
int r=n;
for(int i=0;i<n;i++){
if(s[i]=='0')continue;
if(s[i]=='1'){
int j=i+1;
while (j<n&&s[j]=='1'){
j++;
}
int m=j-i,k=(m-1)/2;
if(!i||j==n){
k=m-1;
}
r=min(k,r);//求最小天数
v.push_back(m);
i=j;
}
}
int res=0;
for(int c:v){
res+=(c+r*2)/(r*2+1);//向上取整;
}
cout<<res<<endl;
return 0;
}
星期四
农夫约翰真的种地
农夫约翰在他的农场种植了 NN 个芦笋,编号 1∼N1∼N。
其中,第 ii 个芦笋的初始高度为 hihi,每经过一天高度会增长 aiai。
给定一个 0∼N−10∼N−1 的排列 t1,t2,…,tNt1,t2,…,tN。
请问至少多少天后能够满足,对于每个 1≤i≤N1≤i≤N,恰好有 titi 个芦笋比第 ii 个芦笋的高度更高。
输入格式
第一行包含整数 TT,表示共有 TT 个测试数据。
每组数据第一行包含整数 NN。
第二行包含 NN 个整数 h1,h2,…,hNh1,h2,…,hN。
第三行包含 NN 个整数 a1,a2,…,aNa1,a2,…,aN。
第四行包含 NN 个整数 t1,t2,…,tNt1,t2,…,tN。
输出格式
每组数据输出一行结果,一个整数表示答案,如果无解则输出 -1。
数据范围
1≤T≤101≤T≤10,
1≤N≤2×1051≤N≤2×105,
1≤hi,ai≤1091≤hi,ai≤109,
0≤ti≤N−10≤ti≤N−1,
保证 t1∼tNt1∼tN 是一个 0∼N−10∼N−1 的排列,
一个测试点内所有的 NN 相加之和不超过 2×1052×105。
输入样例1:
6
1
10
1
0
2
7 3
8 10
1 0
2
3 6
10 8
0 1
2
7 3
8 9
1 0
2
7 7
8 8
0 1
2
7 3
8 8
1 0
输出样例1:
0
3
2
5
-1
-1
样例1解释
第 11 组数据,由于只有一个芦笋,所以不需要经过任何天数,第 00 天即可满足条件。
第 22 组数据,我们需要使得第 11 个芦笋比第 22 个芦笋更矮:
天数 芦笋1 芦笋2
0 7 3
1 15 13
2 23 23
3 31 33
经过 33 天后,即可满足条件。
第 3,43,4 组数据与第 22 组数据类似。
第 55 组数据,两个芦笋的初始高度和生长速度都相同,所以永远保持相同高度,一定无法满足条件。
第 66 组数据,两个芦笋的初始高度不满足条件,生长速度都相同,所以永远不可能满足条件
题解:
区间范围题
纯数学理解题,给定t那么我们就可以知道这个h的排序一定是在t+1个位置,所有设置rank1[t+1]=i,
排好序后,剩下的为从大到小,两练列不等式
h[i]+xa[i]>h[i+1]+xa[i=1]........
k可以解不等式,得到x的取值,但是要分情况讨论,分为a[i]-a[i+1]>0,也就是B,....
x<(h[i+1]-h[i])/a[i+1]-a[i]---->x<=(h[i+1]-h[i])/a[i+1]-a[i]-1;向上取整-1;
x>(h[i+1]-h[i])/a[i+1]-a[i]---->x>=(h[i+1]-h[i])/a[i+1]-a[i]+1;向下取整+1;
B=0时候,也要分,h1>h2;相等就无解。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i <=(e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
const int N=2e5+10,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
int n,h[N],a[N],rank1[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while (T--){
cin>>n;
REP1(i,1,n)cin>>h[i];
REP1(i,1,n)cin>>a[i];
for(int i=1;i<=n;i++){
int t;
cin>>t;
rank1[t+1]=i;
}
int l=0,r=1e9;
for(int i=1;i<n;i++){
int A=h[rank1[i]]-h[rank1[i+1]];
int B=a[rank1[i+1]]-a[rank1[i]];
if(B>0){
r=min(r, (int)ceil((double)A/B)-1);
}else if(B<0){
l=max(l,(int) floor((double )A/B)+1);
}
else if(A<=0){
r=-1;
break;
}
}
if(l>r)l=-1;
cout<<l<<endl;
}
return 0;
}
星期五
多数意见
农夫约翰有一项重要的任务——弄清楚要为他的奶牛们购买什么类型的干草。
农夫约翰的 NN 头奶牛编号为 11 到 NN,每头奶牛喜欢恰好一种类型的干草 hihi。
他希望他的所有奶牛都喜欢同一种干草。
为了实现这一目标,农夫约翰可以主持焦点小组访谈。
每一次焦点小组访谈,约翰都可以自由选择任意多个连续编号的奶牛构成访谈小组,共同参加访谈。
如果有一种干草是小组中超过一半的奶牛喜欢的,则此次焦点小组访谈结束后,组内所有奶牛最终都会喜欢这种干草。
如果不存在这样的干草,那么奶牛们就不会改变她们喜欢的干草类型。
例如,在由 1616 头奶牛组成的焦点小组访谈中,需要有其中 99 头或更多的奶牛具有相同的干草喜好,才能使其余奶牛改变其喜好以与之一致。
农夫约翰想知道哪些类型的干草有可能变为同时受到所有奶牛的喜爱。
他一次只能主持一个焦点小组访谈,但为了使所有奶牛都喜欢同一类型的干草,他可以根据需要任意多次地主持焦点小组访谈。
输入格式
输入的第一行包含一个整数 TT,为独立的测试用例的数量。
每一个测试用例的第一行包含 NN。
第二行包含 NN 个整数,为奶牛们喜爱的干草类型 hihi。
输出格式
输出 TT 行,对于每个测试用例输出一行。
如果可能使所有奶牛同时喜欢同一种干草,则以升序输出所有可能的此类干草的类型,否则输出 -1。
在同一行内输出一列整数时,相邻的数用空格分隔,并确保行末没有多余空格。
数据范围
1≤T≤101≤T≤10,
2≤N≤1052≤N≤105,
1≤hi≤N1≤hi≤N,
输入保证同一测试点内所有测试用例的 NN 之和不超过 2⋅1052⋅105。
输入样例:
5
5
1 2 2 2 3
6
1 2 3 1 2 3
6
1 1 1 2 2 2
3
3 2 3
2
2 1
输出样例:
2
-1
1 2
3
-1
题意
给定一个序列,每次操作可以选择一个区间 [l,r],若存在一个数 x,在区间内出现的次数超过区间长度的一半,则可以将区间所有数变成 x,现在要将序列全部变成一种数,求出序列能变成的数的集合。
题解:
贪心考虑如何用最少的数来完成同化序列,假设如果存在两个连在一起的 x,形如 4 3 2 1 x x 1 2 3 4,每次我们可以选择中间的两个 x,以及他向外延申的任意一个单位,构成形如 x x y 的连续子区间,即可将 y 变成 x,同理向外不断拓展,最终序列会全部变成 x。也就是说,如果出现连续出现的数,她一定会成为答案,那么考虑更加一般的情况,我们考虑要让x出现次数超过一半且x不能连续,那么序列必须形如x y x z x的形式,而其中,只要出现了x y x这样的子区间,我们就可以将y变成x,此时序列中再次出现连续的x,即可构成答案。
综上所述,我们只需要枚举所有数的所有位置,检查其相邻的两个出现位置的距离是否小于2,即可判断是否能构成答案.
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i < (e); ++i)
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
void solve(){
int n;
cin>>n;
vector<vector<int>>pos(n+1);
for(int i=1;i<=n;i++){
int x;
cin>>x;
pos[x].push_back(i);
}
vector<int>ans;
for(int i=1;i<=n;i++){
for(int j=0;j+1<pos[i].size();j++){
if(pos[i][j+1]-pos[i][j]<=2){ans.push_back(i);break;}
}
}
if(ans.size()){
for(auto i:ans){cout << i << " \n"[i == ans.back()];}
}else{
cout<<"-1"<<endl;
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
3月3日-3月九日
星期一
炮弹
贝茜已经精通了变成炮弹并沿着长度为 NN 的数轴弹跳的艺术,数轴上的位置从左到右编号为 1,2,…,N1,2,…,N。
她从某个整数位置 SS 开始,以 11 的起始能量向右弹跳。
如果贝茜的能量为 kk,则她将弹跳至距当前位置向前距离 kk 处进行下一次弹跳。
从 11 到 NN 的每个整数位置上均有炮击目标或跳板。
每个炮击目标和跳板都有一个在 00 到 NN 范围内的整数值。
一个数值为 vv 的跳板会使贝茜的能量增加 vv 并反转她的方向。
一个数值为 vv 的炮击目标会当着陆时能量不小于 vv 时被击破。
着陆在炮击目标上不会改变贝茜的能量和方向。
被击破的炮击目标将保持击破状态,贝茜依然可以在该炮击目标上弹跳,同样不会改变能量和方向。
如果贝茜弹跳无限长的时间直到她离开数轴,她会击破多少个炮击目标?
如果贝茜开始时位于一个她可以击破的炮击目标,她会立刻将其击破。
类似地,如果贝茜开始时位于一个跳板,跳板的效果将在她第一次跳跃之前生效。
输入格式
输入的第一行包含 NN 和 SS,其中 NN 为数轴的长度,SS 为贝茜的起始位置。
以下 NN 行描述了每一个位置。其中第 ii 行包含整数 qiqi 和 vivi,如果位置 ii 上有一个跳板则 qi=0qi=0,位置 ii 上有一个炮击目标则 qi=1qi=1,vivi 是位置 ii 上的跳板或炮击目标的数值。
输出格式
输出一个整数,为将被击破的炮击目标数量。
数据范围
1≤N≤1051≤N≤105,
1≤S≤N1≤S≤N,
0≤qi≤10≤qi≤1,
0≤vi≤N0≤vi≤N
输入样例1:
5 2
0 1
1 1
1 2
0 1
1 1
输出样例1:
1
样例1解释
贝茜从坐标 22 开始,这是一个数值为 11 的炮击目标,所以她立刻击破了它。
然后她弹跳至坐标 33,这是一个数值为 22 的炮击目标,所以她无法击破它。
她继续弹跳至坐标 44,这改变了她的方向并将她的能量增加了 11,达到 22。
她跳回至坐标 22,这是一个已经被击破的炮击目标,所以她继续弹跳。
此时,她弹跳至了坐标 00,因此她停了下来。
她击破了恰好一个炮击目标,位于坐标 22。
输入样例2:
6 4
0 3
1 1
1 2
1 1
0 1
1 1
输出样例2:
3
样例2解释
贝茜经过的路径为 4→5→3→1→64→5→3→1→6,下一次弹跳将会使她离开数轴(1111)。
她依次击破了炮击目标 4,3,64,3,6。
题解:
炮弹(详细思路分析-如何正确跳出死循环)
思路
思路很简单,按照题意模拟炮弹的运动轨迹,可以用一个flag变量,初始值为1,换方向就乘以-1来计算炮弹跳跃后的位置,能量enery一直累加即可
难点
如果存在两个能量为0的跳板(如果能量不为0,那么最后能量累加的很大了,一定会一次就跳出去),那么就有可能存在在两个跳板之间不断来回跳跃的情况。这种情况是需要我们特判的。
什么时候跳出循环?
当我们重复第二次经过一个跳板时,如果此时的能量没有改变,我们就认为这里存在两个跳板使得炮弹进入了死循环。所以我们应该记录每一个起跳点的位置和能量,如果能量变化,就更新能量值,如果能量相等就跳出循环。起跳点的能量就用一个数组来维护就可以了。
时间复杂度
这题分析时间复杂度作练习不错
理想情况存在一个1能量跳板分布
1次能量的情况能跳n/1次
2次能量的情况能跳n/2次
...
x次能量的情况能跳n/x次
...
n次能量的情况能跳1次
∑等于n乘上一个调和级数,所以粗略估计时间复杂度在NlnN以内
N的范围是NlogN能过,NlogN >NlnN,所以模拟就能过
所以也可以特判只需要超过120万次就break就行了
模拟过程,跳出死循环,运用级数的知识得到不能超过n*26的步数;
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
//步数上限;
int n,x;
int q[N],v[N];
bool st[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>x;
REP1(i,1,n)cin>>q[i]>>v[i];
int res=0,cnt=0,e=1,d=1;
while (cnt<n*26){
cnt++;
if(q[x]){
if(e>=v[x]&&!st[x]){
st[x]= true;
res++;
}
}else{
e+=v[x],d=-d;
}
x+=e*d;
if(x<1||x>n){
break;
}
}
cout<<res<<endl;
return 0;
}
星期二
平衡细菌
农夫约翰有 NN 块草地排成一行,其中草地 ii 的细菌水平与健康草的细菌水平相差 aiai。
例如,如果 ai=−3ai=−3,则草地 ii 的细菌水平比正常水平低 33,需要额外添加恰好 33 个单位的细菌才能将其提高到被认为是健康的程度。
农夫约翰想要确保每一块草地都被修复至健康的细菌水平。
方便的是,他有两种品牌的农药可以喷洒在他的田地里,一种可以添加细菌,另一种可以去除细菌。
当农夫约翰喷洒任一类型的农药时,他站在草地 NN(最右边的草地)并为他的喷雾器选择功率等级 LL(1≤L≤N1≤L≤N)。
喷雾器对靠近农夫约翰的草地效果最大,随着距离增加效果逐渐减弱。
如果农夫约翰选择添加细菌的农药,则 LL 单位的细菌将被添加至草地 NN,L−1L−1 单位添加至草地 N−1N−1,L−2L−2 单位添加至草地 N−2N−2,以此类推。
草地 1…N−L1…N−L 不会得到任何细菌,因为喷雾器设置的功率不足以到达它们。
类似地,如果农夫约翰选择去除细菌的农药,则 LL 单位的细菌将被从草地 NN 去除,L−1L−1 单位被从草地 N−1N−1 去除,以此类推。
同样,草地 1…N−L1…N−L 将不受影响。
求农夫约翰使用喷雾器的最少次数,使得每块草地都具有健康草的推荐细菌值。
输入保证答案不超过 109109。
输入格式
输入的第一行包含 NN。
第二行包含 NN 个整数 a1…aNa1…aN,为每块草地的初始细菌水平。
输出格式
输出一个整数,为使每块草地都具有健康草的推荐的细菌值所需使用喷雾器的最少次数。
数据范围
1≤N≤2×1051≤N≤2×105,
−1015≤ai≤1015−1015≤ai≤1015
输入样例1:
2
-1 3
输出样例1:
6
样例1解释
使用去除细菌的农药,功率等级为 11,使用五次。
然后使用添加细菌的农药,功率等级为 22,使用一次。
输入样例2:
5
1 3 -2 -7 5
输出样例2:
26
题意(差分):
差分就是每一项为前面两个数的差
一共有1-N个数,每次操作时,从N向左操作数L(1<=L<=N)减一,很容易想到差分,于是构造一阶差分数组
1 2 3 4 5(原数组)
1 1 1 1 1(一阶差分数组)
此时发现有规律可循,便构造二阶差分数组
1 2 3 4 5(原数组)
1 1 1 1 1(一阶差分数组)
1 0 0 0 0(二阶差分数组)
相当于随意在一个数上选择加一或者减一
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++)c[i]=b[i]-b[i-1];
int res=0;
for(int i=1;i<=n;i++){
res+=abs(c[i]);
}
cout<<res<<endl;
return 0;
}
星期三
回文游戏
贝茜和埃尔茜正在使用一个石子堆进行一个游戏,初始时,石子堆中共有 SS 个石子。
两头奶牛依次行动,贝茜先行动。
当轮到一头奶牛行动时,她必须从堆中取走 xx 个石子,其中 xx 是该奶牛选定的任意正整数回文数。
如果当一头奶牛的回合开始时石子堆是空的,那么这头奶牛就输了。
定义:一个正整数如果从前向后和从后向前读相同,则该数为回文数;回文数的例子有 11,121121 和 90099009。
回文数不允许有前导零;例如,990990 不是回文数。
有 TT 个独立的测试用例。
对于每一个测试用例,输出如果两头奶牛都采取最优策略,谁会赢得游戏。
输入格式
输入的第一行包含 TT,为测试用例的数量。
以下 TT 行为测试用例,每个测试用例一行。
每个测试用例均由一个整数 SS 指定。
输出格式
对于每一个测试用例输出一行,如果贝茜在最优策略下可以从一堆 SS 个石子的石子堆开始赢得游戏,则输出 B,否则输出 E。
数据范围
1≤T≤101≤T≤10,
1≤S<101051≤S<10105
输入样例:
3
8
10
12
输出样例:
B
E
B
样例解释
对于第一个测试用例,贝茜可以在第一次行动中取走所有石子,因为 88 是回文数,使她获胜。
对于第二个测试用例,1010 不是回文数,因此贝茜无法在第一次行动中取走所有石子。无论贝茜第一回合取走多少石子,埃尔茜总能在第二回合取走所有余下的石子,使她获胜。
对于第三个测试用例,可以证明在最优策略下贝茜可以获胜。
题解:
两人取石头,每次可以取走 x个石头,当且仅当 x是一个回文数,轮到谁石头被取完则输,问是否先手必胜。
题解思路
先考虑最简单的情况,所有的个位数一定是回文数,因此每次可以取走 1∼9
1
∼
9
个石头,当石头数量小于 10
10
的时候,先手必胜。
在上面的结论下,我们可以推出,10
10
一定是先手必败,因为先手无法拿走所有石头,而且无论取多少,后手总是能取走所有石头。我们再考虑更加一般的情况,假设石头数为 11∼19
11
∼
19
,那么先手无论如何都可以取走若干个石头,让对手剩下 10
10
个石头,从而使得对手必败。
我们再考虑回文数,首先,每个人必须要拿走大于等于 1
1
个石头,而且拿走的数必须是一个回文数,因此可以断定,这个数一定不是 10
10
的倍数(因为以0结尾的回文数只有0,而不能取0个),也就是说,如果一个数是 10
10
的倍数,那么一定是不会被一次拿走的。
最后,我们可以得出策略,假设这个数不是 10
10
的倍数,且是一个回文数,我们直接先手拿走必胜,否则,我们可以拿走他的个位数,让他变成 10
10
的倍数,这样对手一定无法一次拿完,对手不管拿什么数,都会让原来的数变得不是 10
10
的倍数,我们再重复上述的步骤。这个步骤最终会停止到数字变成一个个位数,而且此时一定是先手操作,因此必胜。而假设原数是 10
10
的倍数,对手可以反过来操作,先手必败。
ps:数字很大,记得用string存
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
void solve(){
string s;
cin>>s;
if(s.back()=='0'){
cout<<"E"<<endl;
}else{
cout<<"B"<<endl;
}
};
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
TESTS{
solve();
};
return 0;
}
星期四
牛奶交换
农夫约翰的 NN 头奶牛排成一圈,使得对于 1,2,…,N−11,2,…,N−1 中的每个 ii,奶牛 ii 右边的奶牛是奶牛 i+1i+1,而奶牛 NN 右边的奶牛是奶牛 11。
第 ii 头奶牛有一个容量为整数 aiai 升的桶。
所有桶初始时都装满了牛奶。
每一分钟,奶牛都会根据一个字符串 s1s2…sNs1s2…sN 传递牛奶,该字符串仅由字符 L 和 R 组成。
当第 ii 头奶牛至少有 11 升牛奶时,如果 si=si=L,她会将 11 升牛奶传递给她左边的奶牛,如果 si=si=R,她会将 11 升牛奶传递给右边的奶牛。
所有交换同时发生(即,如果一头奶牛的桶是满的,送出 11 升牛奶的同时,也收到 11 升牛奶,则她的牛奶量保持不变)。
如果此时一头奶牛的牛奶量超过了桶的容量 aiai,则多余的牛奶会损失。
农夫约翰想要知道:经过 MM 分钟后,所有奶牛总共还余下多少牛奶?
输入格式
输入的第一行包含 NN 和 MM。
第二行包含一个字符串 s1s2…sNs1s2…sN,仅由字符 L 或 R 组成,表示每头奶牛传递牛奶的方向。
第三行包含整数 a1,a2,…,aNa1,a2,…,aN,为每个桶的容量。
输出格式
输出一个整数,为 MM 分钟后所有奶牛总共余下的牛奶量。
数据范围
1≤N≤2×1051≤N≤2×105,
1≤M≤1091≤M≤109,
1≤ai≤1091≤ai≤109
输入样例1:
3 1
RRL
1 1 1
输出样例1:
2
样例1解释
奶牛 22 和 33 互相传递一升牛奶,因此她们的牛奶得以保留。
当奶牛 11 将牛奶传递给奶牛 22 时,奶牛 22 的桶会溢出,从而一分钟后损失了一升牛奶。
输入样例2:
5 20
LLLLL
3 3 2 3 3
输出样例2:
14
样例2解释
每头奶牛都将一升牛奶传递给左边的奶牛,并从右边的奶牛那里获得一升牛奶,因此无论经过多长时间所有牛奶都会被保留下来。
输入样例3:
9 5
RRRLRRLLR
5 8 4 9 3 4 9 5 4
输出样例3:
38
样例3解释
初始时,共有 5151 升牛奶。
55 分钟后,奶牛 33,66 和 77 将分别损失 55,33 和 55 升牛奶。
因此,总共还剩下 3838 升牛奶。
题解:
相当于把这个环上的东西分解为多组连续r+L,断环成链,加n,根据每个rrrrllll只有两段会少1每分钟。
所有可以求和;
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N = 400010;
int n,m;
char str[N];
int w[N];
signed main(){
scanf("%lld%lld%s",&n,&m,str);
int res=0;
REP(i,n){
cin>>w[i];
res+=w[i];
w[i+n]=w[i];
str[i+n]=str[i];
}
int k=0;//分隔点;
while(k<n&&str[k]==str[k+1]){
k++;
}
if(k<n){//双指针
for(int i=k+1;i<=n;i++){
int j=i;
int sum=0;
while (j<=k+n&&str[j]==str[i]){
sum+=w[j];
j++;
}
if(str[i]=='R')sum-=w[j-1];
else sum-=w[i];
res-=min(m,sum);
i=j-1;
}
}
cout<<res<<endl;
return 0;
}
星期五
最大限度地提高生产力
农夫约翰有 NN 个农场,编号为 11 到 NN。
已知农夫约翰会在时刻 cici 关闭农场 ii。
贝茜在时刻 SS 起床,她希望在农场关闭前访问尽可能多的农场,从而最大限度地提高她这一天的生产力。
她计划在时刻 ti+Sti+S 访问农场 ii。
贝茜必须于严格早于农夫约翰关闭农场的时刻抵达农场才能成功进行访问。
贝茜有 QQ 个询问。
对于每个询问,她会给你两个整数 SS 和 VV。
对于每个询问,输出当贝茜在时刻 SS 起床是否可以访问至少 VV 个农场。
输入格式
输入的第一行包含 NN 和 QQ。
第二行包含 c1,c2,…,cNc1,c2,…,cN。
第三行包含 t1,t2,…,tNt1,t2,…,tN。
以下 QQ 行,每行包含两个整数 VV 和 SS。
输出格式
对 QQ 个询问的每一个输出一行,输出 YES(是)或 NO(否)。
数据范围
1≤N≤2×1051≤N≤2×105,
1≤Q≤2×1051≤Q≤2×105,
1≤ci≤1061≤ci≤106,
1≤ti≤1061≤ti≤106,
1≤V≤N1≤V≤N,
1≤S≤1061≤S≤106
输入样例:
5 5
3 5 7 9 12
4 2 3 3 8
1 5
1 6
3 3
4 2
5 1
输出样例:
YES
NO
YES
YES
NO
样例解释
对于第一个询问,贝茜将在时间 t=[9,7,8,8,13]t=[9,7,8,8,13] 访问农场, 因此她在农夫约翰关闭农场之前能准时访问到的只有农场 44。
对于第二个询问,贝茜将无法准时访问到任何农场。
对于第三个询问,贝茜将可以准时访问到农场 3,4,53,4,5。
对于第四个和第五个询问,贝茜将能够准时访问除第一个农场之外的所有农场
题解
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
vector<int>c(n),t(n);
REP(i,n)cin>>c[i];
REP(i,n)cin>>t[i],c[i]-=t[i];
sort(c.begin(),c.end());
while(m--){
int v,s;
cin>>v>>s;
int l=0,r=n;
while (l<r){
int mid=(l+r)>>1;
if(c[mid]>s){
r=mid;
}else{
l=mid+1;
}
}
if(n-r>=v)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
3月10日-3月16日
星期二
沿栅栏散步
农夫约翰有 NN 头奶牛,每头奶牛都喜欢日常沿着包围牧场的栅栏散步。
栅栏由 PP 根柱子组成(PP 为偶数),每根柱子的位置是农夫约翰农场地图上的一个不同的二维坐标点 (x,y)(x,y)。
每根柱子通过垂直或水平线段的栅栏连接到两根相邻的柱子,因此整个栅栏可以被视为各边平行于 xx 轴或 yy 轴的一个多边形(最后一根柱子连回第一根柱子,确保围栏形成一个包围牧场的闭环)。
栅栏多边形是「规则的」,体现在栅栏段仅可能在其端点处重合,每根柱子恰好属于两个栅栏段,同时每两个在端点处相交的栅栏段都是垂直的。
每头奶牛的日常散步都有一个偏好的起始和结束位置,均为沿栅栏的某个点(可能在柱子处,也可能不在)。
每头奶牛日常散步时沿着栅栏行走,从起始位置开始,到结束位置结束。
由于栅栏形成闭环,奶牛有两条路线可以选择。
由于奶牛是一种有点懒的生物,每头奶牛都会选择距离较短的方向沿栅栏行走(如果并列,奶牛可以选择任一方向)。
输入格式
输入的第一行包含 NN 和 PP。
以下 PP 行的每一行包含两个整数,以顺时针或逆时针顺序表示栅栏柱子的位置。
以下 NN 行的每一行包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一头奶牛的起始位置 (x1,y1)(x1,y1) 和结束位置 (x2,y2)(x2,y2)。
输出格式
输出 NN 个整数,为每头奶牛行走的距离。
数据范围
1≤N≤1051≤N≤105,
4≤P≤2×1054≤P≤2×105,
0≤x,y≤10000≤x,y≤1000
输入样例:
5 4
0 0
2 0
2 2
0 2
0 0 0 2
0 2 1 0
2 1 0 2
1 0 1 2
1 2 1 0
输出样例:
2
3
3
4
4
样例解释
第一头奶牛可以直接从 (0,0)(0,0) 走到 (0,2)(0,2)。
第二头奶牛可以从 (0,2)(0,2) 走到 (0,0)(0,0),然后走到 (1,0)(1,0)。
第四头奶牛有两条长度相等的可能路线:(1,0)→(0,0)→(0,2)→(1,2)(1,0)→(0,0)→(0,2)→(1,2) 和 (1,0)→(2,0)→(2,2)→(1,2)(1,0)→(2,0)→(2,2)→(1,2)。
题解:
我们只需要算出每个点到初始位置的距离,然后相减,还要把周长算一下,取最小值,模拟就行了。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
int n,m;
int d[M][M];
int fist_x,fist_y;//初始位置;
int last_x,last_y;
int prem;//这个计算周长;
void update(int x,int y){//计算每个点到起点的距离;
int dx=x-last_x,dy=y-last_y;
int step=abs(dx)+abs(dy);
dx/=step,dy/=step;
while(step--){
last_x+=dx,last_y+=dy;
d[last_x][last_y]=++prem;
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>fist_x>>fist_y;
last_x=fist_x,last_y=fist_y;
while(--m){
int x,y;
cin>>x>>y;
update(x,y);
}
update(fist_x,fist_y);//d[fist_x][fist_y]=0;
while(n--){
int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;
int dist=abs(d[x1][y1]-d[x2][y2]);
cout<<min(dist,prem-dist)<<endl;
}
return 0;
}
星期三
领导者
有 NN 头奶牛站成一排,从左到右依次编号为 1∼N1∼N。
每头奶牛要么是根西牛(用 G 表示),要么是荷斯坦牛(用 H 表示)。
每头奶牛都写下了一份奶牛名单,其中第 ii 头奶牛写下的名单中包含 [i,Ei][i,Ei](i≤Ei≤Ni≤Ei≤N)范围内的所有奶牛。
现在,我们要在每个品种的奶牛当中选出一个领导者。
要求,每个领导者写下的奶牛名单应满足以下两个条件中的至少一个:
- 名单中包含其品种的所有奶牛。
- 名单中包含另一品种的领导者。
请问,一共有多少个满足条件的选择方案。
输入格式
第一行包含整数 NN。
第二行包含一个长度为 NN 的由 G 和 H 构成的字符串,其中第 ii 个字符表示第 ii 头奶牛的品种(G 为根西牛,H 为荷斯坦牛)。
第三行包含 NN 个整数 E1,E2,…,ENE1,E2,…,EN。
输出格式
一个整数,表示满足条件的选择方案数量。
数据范围
2≤N≤1052≤N≤105,
i≤Ei≤Ni≤Ei≤N,
保证根西牛和荷斯坦牛的数量均不小于 11。
保证至少存在一个满足条件的选择方案。
输入样例1:
4
GHHG
2 4 3 4
输出样例1:
1
样例1解释
唯一满足条件的方案是选择奶牛 11 和奶牛 22 作为领导者,奶牛 11 的名单中包含奶牛 22(另一品种的领导者),奶牛 22 的名单中包含所有同品种奶牛。
其它方案均不满足条件,例如,选择奶牛 22 和奶牛 44 作为领导者就不可行,因为奶牛 44 的名单中既不包含另一品种的领导者,也不包含所有同品种奶牛。
输入样例2:
3
GGH
2 3 3
输出样例2:
2
样例2解释
一共有 22 种满足条件的选择方案:
- 选择奶牛 11 和奶牛 33。
- 选择奶牛 22 和奶牛 33。
题解:
有一个领导可以管理自己所有种类的牛,且另外一个领导可以管理这个领导。
两个领导都可以管理各自种类的牛。
把这个想出来代码也就自然好写了。
我们可以先把两种种类第一次和最后一次出现的牛出现的位置找出来。
之后我们可以看到,如果这头牛能管控的牛可以当领导(那么这个被管控的领导管控的牛必须包括所有和自己种类一样的牛),如果满足,那么就形成一对。
如果两头牛都可以管控所有和自己种类一样的牛,那么也是一对。
注:另外需要注意,如果之前两头牛已经成为一组,则不算。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
string st;
cin>>st;
st=' '+st;
int g=0,h=0,G=0,H=0;
//记录同种牛第一次出现的位置,
for(int i=1;i<st.size();i++){
if(st[i]=='G'&&!g)g=i;
else if(st[i]=='H'&&!h)h=i;
}//记录同种牛最后出现的位置,
for(int i=st.size()-1;i>=1;i--){
if(st[i]=='G'&&!G)G=i;
else if(st[i]=='H'&&!H)H=i;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
char ch;
int ans=0;
bool gg=0,hh=0;
for(int i=1;i<=n;i++){//有一个领导可以管理自己所有种类的牛,且另外一个领导可以管理这个领导。
if(st[i]=='G'){
if(a[i]>=h&&i<=h&&a[h]>=H){
ans++;
if(i==g)gg=1;
}
}
if(st[i]=='H'){
if(a[i]>=g&&i<=g&&a[g]>=G){//有一个领导可以管理自己所有种类的牛,且另外一个领导可以管理这个领导。
ans++;
if(i==h)hh=1;
}
}
}
if(a[g]>=G&&a[h]>=H&&!gg&&!hh)ans++;
cout<<ans;
return 0;
}
星期四
空调II
炎炎夏日,酷热难耐,农夫约翰计划打开牛棚中的空调给奶牛降温。
约翰的牛棚中一共有 NN 头奶牛,编号 1∼N1∼N。
牛棚中有 100100 个牛栏排成一排,编号依次为 1∼1001∼100。
每头奶牛都占据着连续若干个牛栏,同一个牛栏最多被一头奶牛占据。
第 ii 头奶牛占据的牛栏范围是 [si,ti][si,ti]。
不同奶牛的降温需求不同,第 ii 头奶牛的降温需求为 cici,这意味着它占据的每个牛栏都至少需要降温 cici。
牛棚中一共有 MM 台空调,编号 1∼M1∼M。
第 ii 台空调的运行成本为 mimi,开启这台空调可以让 [ai,bi][ai,bi] 范围内的每个牛栏降温 pipi。
不同空调的覆盖范围可能重叠,降温效果也可以叠加。
约翰希望在让所有奶牛的降温需求都得到满足的前提下,花费的总成本尽可能小。
输出所需总成本的最小可能值。
输入格式
第一行包含 N,MN,M。
接下来 NN 行,每行包含三个整数 si,ti,cisi,ti,ci。
接下来 MM 行,每行包含四个整数 ai,bi,pi,miai,bi,pi,mi。
输出格式
一个整数,表示所需总成本的最小可能值。
数据范围
1≤N≤201≤N≤20,
1≤M≤101≤M≤10,
1≤si≤ti≤1001≤si≤ti≤100,
1≤ci≤1061≤ci≤106,
1≤ai≤bi≤1001≤ai≤bi≤100,
1≤pi≤1061≤pi≤106,
1≤mi≤10001≤mi≤1000
输入样例:
2 4
1 5 2
7 9 3
2 9 2 3
1 6 2 8
1 2 4 2
6 9 1 5
输出样例:
10
样例解释
一种满足条件的最佳方案为运行第 1,3,41,3,4 个空调,所需成本为 3+2+5=103+2+5=10
题解:
直接暴力搜索,到底选不选这个数,如果选那就进行操作。dfs
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
//int a[N],b[N],c[N],pre[N];
int n,m;
int b[M];
int ans=2e9,mx;
struct cow{//结构体
int s,t,c;
}a[25];
struct ac{
int a,b,p,h;
}ac[15];
void dfs(int u,int num){
if(u>m){
for(int i=1;i<=mx;i++){
if(b[i]>0)return;
}
ans=min(ans,num);//如果全部牛棚小于0,说明这个方法可以更新
return;
}
dfs(u+1,num);//不使用这个空调;
for(int j=ac[u].a;j<=ac[u].b;j++)b[j]-=ac[u].p;
dfs(u+1,num+ac[u].h);//选这个空调;
//回溯
for(int j=ac[u].a;j<=ac[u].b;j++)b[j]+=ac[u].p;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
REP1(i,1,n){
cin>>a[i].s>>a[i].t>>a[i].c;
for(int j=a[i].s;j<=a[i].t;j++)b[j]=a[i].c;
mx=max(mx,a[i].t);//记录有牛的最右端。
}
for(int i=1;i<=m;i++){
cin>>ac[i].a>>ac[i].b>>ac[i].p>>ac[i].h;
}
dfs(1,0);
cout<<ans<<endl;
return 0;
}
星期五
面包店
贝茜开了一家面包店。
贝茜的面包店中只有一个烤箱,该烤箱制作一块饼干需要花费的时间为 tCtC,制作一块松饼需要花费的时间为 tMtM。
烤箱每次只能制作一个糕点,也就是说制作 AA 块饼干和 BB 块松饼需要花费的时间为 A×tC+B×tMA×tC+B×tM。
有 NN 个客人来光顾贝茜的生意,编号 1∼N1∼N。
客人是一个接着一个来的,并且第 i+1i+1 个客人一定会在第 ii 个客人走后才会来。
第 ii 个客人想要 aiai 块饼干和 bibi 块松饼。
为了保证售出糕点足够新鲜,贝茜一定会在客人下单后才开始现场制作其所需要的全部糕点。
每个客人的耐心都是有限的,对于第 ii 个客人,如果贝茜制作其所需全部糕点花费的总时间超过 cici,那么它就会生气离开。
贝茜不希望得罪任何客人,在第 11 个客人到来之前,它可以选择对它的烤箱进行升级。
烤箱每升一级,可以进行如下选择:要么使得生产一块饼干所需的时间减少 11,要么使得生产一块松饼所需的时间减少 11。
注意:不论如何升级烤箱,生产饼干或松饼的时间至少为 11。
请你计算,为了让所有客人都能满意,至少需要升多少级烤箱。
输入格式
第一行包含整数 TT,表示共有 TT 组测试数据。
每组数据第一行是空行。
第二行包含三个整数 N,tC,tMN,tC,tM。
接下来 NN 行,每行包含三个整数 ai,bi,ciai,bi,ci。
输出格式
每组数据输出一行结果,一个整数,表示烤箱需要的最小升级数量。
数据范围
1≤T≤1001≤T≤100,
1≤N≤1001≤N≤100,
1≤tC,tM≤1091≤tC,tM≤109,
1≤ai,bi≤1091≤ai,bi≤109,
ai+bi≤ci≤2×1018ai+bi≤ci≤2×1018
输入样例:
2
3 7 9
4 3 18
2 4 19
1 1 6
5 7 3
5 9 45
5 2 31
6 4 28
4 1 8
5 2 22
输出样例:
11
6
样例解释
对于第一组数据,贝茜可以对烤箱进行 1111 次升级,其中 44 次减少饼干制作时间,77 次减少松饼制作时间,升级过后制作一块饼干的时间为 33,制作一块松饼的时间为 22。
这样的话,完成第 11 个客人的订单需要花费的总时间为 1818,完成第 22 个客人的订单需要花费的总时间为 1414,完成第 33 个客人的订单需要花费的总时间为 55,所有人都可以满意离开。
对于第二组数据,贝茜可以对烤箱进行 66 次升级,全部用于减少饼干制作时间
题解
由题意知道
a(tc-x)+b(tm-y)<=ci;
并且明显知道升级的次数越大,一定能完成,所有可以使用二分
让x+y=mid;
y=mid-x;
所有有不等式;
1...a(tc-x)+b(tm-mid+x)<=c;
(b-a)x<=c-atc-b(tm-mid);
并且:
2....0《=x<=tc-1;
0<=y<=tm-1;
y=mid-x;
3...所以m+mid<=x<=mid;
这三个等式列数字;
只要让x有解就可以
所有二分答案,mid让他也解就可以
代码
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
#define yn(ans) printf("%s\n", (ans)?"Yes":"No");//快速打印
#define YN(ans) printf("%s\n", (ans)?"YES":"NO");
#define REP(i, e) for (int i = 0; i < (e); ++i)
#define REP1(i, s, e) for (int i = (s); i <=(e); ++i)
#define TESTS int t; cin >> t; while (t--)
#define TEST
const int N=2e5+10,M=1e3+10,mod=1e9+7;
int a[N],b[N],c[N],pre[N];
int n,tc,t2;
bool check(int mid){
int minx=max(0ll,mid+1-t2),maxx=min(mid,tc-1ll);
for(int i=0;i<n;i++){
double l=b[i]-a[i];
double r=c[i]-(double)a[i]*tc-(double)b[i]*(t2-mid);//double可以防止报空间。
if(l==0&&r<0)return false;
else if(l>0)maxx=min(maxx,(int)floor(r/l));//向上取整;
else if(l<0)minx=max(minx,(int) ceil(r/l));//向下取整;
}
return minx<=maxx;
}
void solve(){
cin>>n>>tc>>t2;
for(int i=0;i<n;i++){
cin>>a[i]>>b[i]>>c[i];
}
int l=0,r=tc+t2-2;
while (l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<endl;
};
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
TESTS{
solve();
};
return 0;
}

浙公网安备 33010602011771号