官方题解链接:点击这里传送
Codeforces 1555A 题目链接:点击这里传送
题意:
你要买n个披萨,但是披萨店只能花15分钟烤6个披萨,20分钟烤8个披萨,25分钟烤10个披萨,你只能选择上述的三种形式。
思路:
仔细观察就能发现,无论哪种方式,烤披萨的时间都是2.5分钟1个。就转变成了类似于出租车计费的问题,起步价是15,后面每烤2个披萨(向上取整)再加5。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
ll n;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t;
while (t--)
{
ll ans = 0;
cin >> n;
if (n <= 6)
{
ans = 15;
}
else//每过两元加五块
{
ll m = 1;
ans = 15 + (1 + (n - 6-1) / 2) * 5;
}
cout << ans << endl;
}
return 0;
}
Codeforces 1555B 题目链接:点击这里传送
题意:
给出一个房间的长和宽,桌子A的长和宽,桌子B的长和宽,如果A和B能挤在同一房间里,A最少需要移多少距离,如果不行输出-1。
思路:
结论一: 如果两个桌子能挤到一间房子里,那么他们的长之和与宽之和起码有一个是小于等于房间的长和宽的。
根据这个结论,我们可以做个特判,把那些不符合结论的直接剔除。另外根据结论,A最多只需要移一次,我们就把两个方向所要移动的最小距离做个比较输出就行了不懂这题为什么这么多人没过
#include<bits/stdc++.h>
using namespace std;
double x, y, n, m, a, b,xx,yy,w,h;
int t;
double ans;
int main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t;
while (t--)
{
cin >> n >> m;
cin >> x >> y >> xx >> yy;//左下角坐标和右上角坐标
a = xx - x; b = yy - y;//长和宽
cin >> w >> h;
ans = 9999999999;
if (w > n || a > n || h > m || b > m)
{
cout << -1 << endl;
continue;
}
//分成能挤一挤和不能挤一挤两种情况
if (h + b <= m)//竖的能挤一挤
{
double dy = max(y, m - yy)+0.0;
if (dy >= h) ans = 0.0;
else ans = h - dy+0.0;
}
if (w + a <= n)//横的能挤一挤
{
int dx = max(x, n - xx)+0.0;
if (dx >= w) ans = 0.0;
else ans = min(ans, w - dx)+0.0;
}
if (h + b > m && w + a > n)
{
cout << -1 << endl;
continue;
}
printf("%.6lf\n", ans);
}
return 0;
}
Codeforces 1555C 题目链接:点击这里传送
题意:
有一个2
×
\times
×N的棋盘,每个点上有一些权值,当某个人经过某个点后,他的分数会加上权值然后那个地方的权值会重置为0。Alice和Bob都是从点(1,1)开始,到点(2,n)结束。Alice先手,他的目标是让Bob的分尽可能低(自己的分无所谓),Bob的目标是使自己的分数尽可能高。假设他们都完成了各自的最优策略,求Bob最后的分数是多少。
思路:
不多说,直接上图:
维护两个前缀和。Alice的目标是让max(sum1[n]-sum1[i],sum2[i-1])尽可能小,这个值同时也就是Bob最优策略下的分数
#include<bits/stdc++.h>
using namespace std;
int t, n;
#define MAXN 100005
int mp[3][MAXN];
long long suma[MAXN];
long long sumb[MAXN];
int pos;
long long mins;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t;
while (t--)
{
cin >> n;
memset(suma, 0, sizeof(suma));
memset(sumb, 0, sizeof(sumb));
mins = 999999999;
for (int i = 1; i <= 2; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> mp[i][j];
if (i == 1) suma[j] = suma[j - 1] + mp[i][j];
else sumb[j] = sumb[j - 1] + mp[i][j];
}
}
for (int i = 1; i <= n; i++)
{
//cout <<"上面"<< sumb[i - 1] << " 下面" << suma[n] - suma[i] << endl;
if (max(sumb[i - 1], suma[n] - suma[i]) < mins)
{
mins = max(sumb[i - 1], suma[n] - suma[i]) ;
pos = i;//从这里分道扬镳
}
}
cout << mins << endl;
}
return 0;
}
Codeforces 1555D 题目链接:点击这里传送
题意:
给你一个只含有abc这三个字母的字符串,任意选定一个区间,若要使这个区间的任意子区间(包括自身)没有回文串,至少需要删除几个字符。
思路:
如果满足题意,这个没有回文的字符串只有可能是以下六种字符串的循环
abc acb bac bca cab cba
可以自己组合看看,无论怎样组合,总有一个子区间是回文的。预处理完任意区间的6种不同字符串对应的情况,查询的时候找最小值就行
#include<bits/stdc++.h>
using namespace std;
string dic[7];//只能根据这个模板写
#define MAXN 200005
long long dp[10][MAXN],ans;
int n,m,l,r;
string s;
void pre()
{
dic[1]="abc";dic[2]="acb";dic[3]="bac";dic[4]="bca";dic[5]="cab";dic[6]="cba";
for(int i=1;i<=6;i++)
{
for(int j=0;j<s.length();j++)
{
if(s[j]!=dic[i][j%3]) dp[i][j+1]=dp[i][j]+1;//这里写j+1纯粹是因为题目给的l和r是从1开始的,要是j的话又要特判防越界还要记得l-1,r-1,麻烦
else dp[i][j+1]=dp[i][j];
}
}
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
cin>>s;
pre();
while(m--)
{
ans=9999999999;
cin>>l>>r;
for(int i=1;i<=6;i++) ans=min(ans,dp[i][r]-dp[i][l-1]);
cout<<ans<<endl;
}
return 0;
}
Codeforces 1555E 题目链接:点击这里传送
题意:
一共有m条边n个点。在某条边的[l,r]这个范围内可以任意通行(可以覆盖重叠)。在可以从1通行到n的情况下,求边的权值的最大值与最小值差的最小值。
思路:
尺取法+线段树维护连通性
参考资料:
关于尺取法的介绍及应用本人就不赘述主要是太菜讲不清楚,直接引用某位大佬的解释:点击这里传送
博客作者 lxt_Lucia的个人主页: 点击这里传送
B站某大佬关于此题的视频讲解:点击这里传送
坑点:
对于普通的线段树处理而言,[l,r]和[r+1,rr]是连通的,但是这题不是
解决方案:
令每个区间的右端点值减1,把维护[1,n]的连通性转变为维护[1,n-1]的连通性
正文:
首先按边的权值排序,做好预处理。先使用线段树维护每个区间每个点出现的最小次数,直到加入某条边后1~n连通(tree[1].val=1)。之后把该边删除,以该边为起点进行尺取法处理。每次加入一条权值较大的边,再尽可能多地删除权值较小的边直到无法连通或没有边为止,记录下此时权值最大的边与权值最小的边的差值。接着继续这样进行尺取操作,直到最后加入了权值最大的边。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005//节点数量
#define MAXM 300005//边的数量
int val[MAXN*4];//每个区间每个点出现的最小次数
int n,m;
struct node
{
int l,r,val;
}a[MAXM];
bool cmp(node a,node b){return a.val<b.val;}//按权值排序
struct segtreenode
{
int l,r;//记录某一区间的最小值
int lazy;
}tree[MAXN*4];
void pushup(int rt){val[rt]=min(val[rt*2],val[rt*2+1]);}
void build(int rt,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
if(l==r) return;
int mid=(l+r)/2;
if(mid>=l) build(rt*2,l,mid);
if(mid<r) build(rt*2+1,mid+1,r);
}
void pushdown(int rt)
{
if(tree[rt].lazy)
{
tree[rt*2].lazy+=tree[rt].lazy;
tree[rt*2+1].lazy+=tree[rt].lazy;
val[rt*2]+=tree[rt].lazy;
val[rt*2+1]+=tree[rt].lazy;
tree[rt].lazy=0;
}
}
void update(int rt,int l,int r,int value)
{
if(tree[rt].l>r||tree[rt].r<l) return;
if(tree[rt].l>=l&&tree[rt].r<=r)
{
val[rt]+=value;
tree[rt].lazy+=value;
return;
}
pushdown(rt);
update(rt*2,l,r,value);
update(rt*2+1,l,r,value);
pushup(rt);
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>m>>n;
n--;//特殊处理,毒瘤题目
for(int i=1;i<=m;i++)
{
int l,r,val;
cin>>a[i].l>>a[i].r>>a[i].val;
a[i].r--;//毒瘤题目的特殊处理
}
sort(a+1,a+1+m,cmp);
build(1,1,n);//个人习惯于把区间端点放入线段树结构体中,本体不build无任何影响
int ans=9999999;
int l=1,r=0;
while(++r<=m)
{
update(1,a[r].l,a[r].r,1);
if(val[1]!=0) break;//如果1~n连通了
}
update(1,a[r].l,a[r].r,-1);//把这条边删了,从这条边开始进行尺取操作
r--;
while(++r<=m)
{
update(1,a[r].l,a[r].r,1);
while(1)
{
update(1,a[l].l,a[l].r,-1);//尽可能的按照权值大小删除边
l++;
if(val[1]==0||l>r) break;//如果不连通了或者没有边了
}
l--;//复原
update(1,a[l].l,a[l].r,1);//复原
ans=min(ans,a[r].val-a[l].val);//记录并更新答案
}
cout<<ans;
return 0;
}