Educational Codeforces Round 112 (Rated for Div. 2)
A. Perfectly Imperfect Array
我是傻逼。卡了40分钟。大力猜错了。一开始想法是预处理120(6,8,10的公倍数)以内的最优解答案。然后对大于120的输入处理一下输出就好。结果WA了==。
考虑到无论那种方法每片披萨的代价一样,也就是说答案就是披萨片数乘2.5。于是只要找大于等于n的最小可拼凑整数即可。然后发现大于等于6的偶数都可以拼凑出来,于是对小于6的特判,大于等于6的,偶数直接乘2.5,奇数+1乘2.5。
ll aa[40005];
int main(){
int t;
cin>>t;
while(t--){
ll n;
cin>>n;
if(n<=6) cout<<15;
else if(n&1) cout<<(n+1)/2*5;
else cout<<n/2*5;
cout<<'\n';
}
}
B. Two Tables
首先把第一个桌子等价转化到靠右上角的位置,那么把二号桌子往左下角接触原点处塞绝对是最优的,一号桌子最优解肯定是只往上或往右平移,所以对两个方向求出需要移动的最小距离。如果一号桌子在两种情况都会越界,那么无解,否则就可以求出最小距离。
int xt,yt,x1,x2,y2,w1,h1,w2,h2,y3;
void change(){
int tmp;
if(y3<yt-y2){
tmp=y3;
y3=yt-y2;
y2=yt-tmp;
}
if(x1<xt-x2){
tmp=x1;
x1=xt-x2;
x2=xt-tmp;
}
}
void solve(){
cin>>xt>>yt;
cin>>x1>>y3>>x2>>y2;
w1=x2-x1; h1=y2-y3;
cin>>w2>>h2;
change();
// cout<<'+'<<x1<<' '<<y3<<' '<<x2<<' '<<y2<<'\n';
int ans=2e9;
int up=-min(0,y3-h2),rig=-min(0,x1-w2);
// cout<<'-'<<up<<' '<<rig<<'\n';
if(up==0||rig==0) ans=0;
else{
if(y2+up<=yt) ans=min(ans,up);
if(x2+rig<=xt) ans=min(ans,rig);
}
if(ans==2e9) cout<<-1<<'\n';
else cout<<fixed<<setprecision(9)<<(double)ans<<'\n';
}
int main(){
int t;
cin>>t;
while(t--)
solve();
return 0;
}
C. Coin Rows
容易想到无论A怎么走,B要么一直往右走到尽头,最后往下走,或,先往下走再往右走到尽头是最优的。A要尽可能缩小这个最优值。那么我们只需要对第一行处理后缀和,第二行处理前缀和,然后枚举列即可。
int aa[3][100005];
int main(){
IOS;
int t;
cin>>t;
while(t--){
int n;
cin>>n;
for(int i=1;i<=2;++i){
for(int j=1;j<=n;++j){
cin>>aa[i][j];
}
}
for(int j=1;j<=n;++j) aa[2][j]+=aa[2][j-1];
for(int j=n;j>=1;--j) aa[1][j]+=aa[1][j+1];
int mn=2e9;
for(int i=1;i<=n;++i){
if(max(aa[1][i+1], aa[2][i-1])<mn){
mn=max(aa[1][i+1], aa[2][i-1]);
}
}
fill(aa[1]+1,aa[1]+n+1,0);
fill(aa[2]+1,aa[2]+n+1,0);
cout<<mn<<'\n';
}
return 0;
}
D. Say No to Palindromes
A题被卡了,导致D没时间写了==。其实也简单。
我们试着构造题目要求的美丽字符串,发现只能形如:
- abcabcabc...
- acbacbacb...
- bcabcabca...
- ...
共计六种情况。那么我们把这六种情况分别与原串匹配,预处理出需要改变的前缀和。之后询问时直接输出六种情况的最小值就好。
string str;
int aa[200005][7];
int main(){
string s[10];
s[1]="abc",s[2]="acb",s[3]="bac",s[4]="bca",s[5]="cab",s[6]="cba";
int n,m;
cin>>n>>m>>str;
for(int i=0;i<n;++i){5
for(int j=1;j<=6;++j){
aa[i+1][j]=aa[i][j];
if(str[i]!=s[j][i%3]) ++aa[i+1][j];
}
}
int l,r;
while(m--){
cin>>l>>r;
cout<<min({aa[r][1]-aa[l-1][1],aa[r][2]-aa[l-1][2],aa[r][3]-aa[l-1][3],aa[r][4]-aa[l-1][4],aa[r][5]-aa[l-1][5],aa[r][6]-aa[l-1][6]});
cout<<'\n';
}
return 0;
}
E. Boring Segments
头一次补到第五题,也说明这场比较简单。。。哎
给一个全为0的段,给很多个子段。。一眼就想到线段树啦。。。
首先想到最小的代价将序列全覆盖,但是全覆盖并不能保证从1可以走到n.
转化一下题目条件。
给定段[l, r],段内任何一点可以可以到达任何一点。可以转化为,给定段[l,r-1],段内和r任何一点可以到达[l, r]。这样可以把[l, r]转化为[l, r-1]。
想一下,原段长为n, 如果[1, n-1]都可以被转化后的段覆盖,那么1肯定是可以到n的。如果[1, n-1]中间有一个点没有被覆盖,那么1肯定是到不了n的。
我们只关心[1,n-1]是否被覆盖, 那么我们用线段树维护这个区间即可。
但是代价是所选择的子段的价值之差的绝对值的最小值。那么从小到达排序,从小到大调,如果每个时刻被[1, n-1]被完全覆盖了,那么就从小到大弹出字段知道没有被完全覆盖...总之是个贪心的想法。证明不难。
就线段树而言我们维护的是最小值。由于存在着区间修改,所以要用lazytag。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ll MAXN=1e18;
const int MOD=1e6;
struct Tree{
#define lchild rt<<1
#define rchild rt<<1|1
int date[4000005];
int tag[4000005];
void push_down(int rt){
date[lchild]+=tag[rt];
date[rchild]+=tag[rt];
tag[lchild]+=tag[rt];
tag[rchild]+=tag[rt];
tag[rt]=0;
}
void push_up(int rt){
date[rt]=min(date[lchild],date[rchild]);
}
void update(int rt, int lef,int rig, int L, int R, int c){
if(lef>=L&&rig<=R){
date[rt]+=c;
tag[rt]+=c;
return;
}
push_down(rt);
int mid=(lef+rig)>>1;
if(mid>=L) update(lchild, lef, mid, L, R, c);
if(mid+1<=R) update(rchild, mid+1, rig, L, R, c);
push_up(rt);
}
int query(int rt, int lef, int rig, int L, int R){
if(lef>=L&&rig<=R){
return date[rt];
}
push_down(rt);
int mid=(lef+rig)>>2;
int ans=2e9;
if(mid>=L) ans=min(ans, query(lchild, lef, mid, L, R));
if(mid+1<=R) ans=min(ans, query(rchild, mid+1, rig, L, R));
return ans;
}
}tree;
struct NODE{
int lef,rig,w;
}aa[300005];
bool cmp(NODE &a, NODE &b){
return a.w<b.w;
}
int main(){
int n,m;
cin>>m>>n;
--n;
for(int i=1;i<=m;++i){
cin>>aa[i].lef>>aa[i].rig>>aa[i].w;
--aa[i].rig;
}
sort(aa+1, aa+1+m, cmp);
int pos=0,ans=2e9;
for(int i=1;i<=m;++i){
tree.update(1, 1, n, aa[i].lef, aa[i].rig, 1);
if(tree.date[1]){
while(tree.date[1]){
++pos;
tree.update(1, 1, n, aa[pos].lef, aa[pos].rig, -1);
}
ans=min(ans, aa[i].w-aa[pos].w);
}
}
cout<<ans<<'\n';
return 0;
}