前言
一场对中国人特别友好地时间场,我上紫了!
开场前睡了一会感觉做A题的时候特别不清醒,一个变量名打错调了两分钟,5min1A,之后看B发现特别水,8min1A,然后开始看C,实在想不好两个长方形相交怎么处理,感觉要写特别多,之后去看D,D翻译完感觉更难,回来先写C不相交的情况,之后突然想到排序之后取中间的值就是相交,然后码码码,47min1A,之后去开D,直接想到做法,但是我的做法边界条件过于恶心,处于半放弃状态码码码,加特判特判,109min1A,但是A之后感觉自己会fst,最后竟然就过了,真切的感觉到自己变强了一些,继续补题吧,想留在紫名可能很难啊。
rating+=60 1864->1924
A. Petya and Origami
题意
有三种颜色的笔记本要购买,A需要2n个,B需要5n个,C需要8*n个,每次可以买k个相同颜色的笔记本,问最少需要买多少次笔记本
做法
每种颜色是独立的,暴力算就好
代码
#include<stdio.h>
typedef long long ll;
ll cal(ll a,ll b)
{
return a/b+(a%b!=0);
}
int main()
{
ll n,k;
scanf("%lld%lld",&n,&k);
ll ans=0;
ans=ans+cal(n*2LL,k);
ans=ans+cal(n*5LL,k);
ans=ans+cal(n*8LL,k);
printf("%lld\n",ans);
return 0;
}
B. Margarite and the best present
题意
给你一个1,-2,3,-4,这样的序列,然后给l,r,求l到r之间的和
做法
很明显相邻两项相加为1,之后就暴力就好了。
代码
#include<stdio.h>
typedef long long ll;
ll cal(ll x)
{
if(x%2==0) return x/2;
else return x/2-x;
}
int main()
{
int q;
scanf("%d",&q);
while(q--)
{
ll l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",cal(r)-cal(l-1));
}
return 0;
}
C. Masha and two friends
题意
给你一个n*m的棋盘,最初(1,1)上为白色,而且每个相邻的块颜色都不同。
之后有两次操作,
第一次操作给出x1,y2,x2,y2
将(x1,y1,x2,y2)这个矩形涂为白色
第二次操作给出x3,y3,x4,y4
将(x3,y3,x4,y4)这个矩形涂为黑色
后涂得会覆盖之前的颜色。问最终的棋盘上黑色和白色的个数
做法
其实做法就是暴力的算,我的做法是写两个函数
第一个函数是cal(x,y)
用来计算原来的棋盘上,(1,1,x,y)这个矩形中黑色白色点的个数,返回值是pair<long long ,long long>类型的
第二个函数是cal2(x,y)
用来计算原来的棋盘上,(x1,y1,x2,y2)这个矩形中黑色白色点的个数,返回值是pair<long long ,long long>类型的,这个是基于容斥用cal去计算的
之后就暴力算就可以。但是要注意的是计算相交的时候,我们将两个举行的x,y全都放进一个数组进行排序,之后取出中间的两个x,y,就是相交的那个矩形,这点很重要,不然要进行很多处理。
代码
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair <ll, ll> pll;
pll cal(ll x,ll y)//白黑
{
return pll(x*y-(x*y)/2,(x*y)/2);
}
ll x[5],y[5];
pll cal2(ll x1,ll y1,ll x2,ll y2)
{
pll ans=cal(x2,y2);
pll ans1=cal(x2,y1-1);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans1=cal(x1-1,y2);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans1=cal(x1-1,y1-1);
ans.first+=ans1.first;
ans.second+=ans1.second;
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll n,m;
scanf("%lld%lld",&n,&m);
ll x_1,y_1,x_2,y_2;
ll x_3,y_3,x_4,y_4;
pll ans=cal(n,m);
scanf("%lld%lld%lld%lld",&x[0],&y[0],&x[1],&y[1]);
scanf("%lld%lld%lld%lld",&x[2],&y[2],&x[3],&y[3]);
if (max(x[0],x[1])<min(x[2],x[3]) || min(x[0],x[1])>max(x[2],x[3]) || max(y[0],y[1])<min(y[2],y[3]) || min(y[0],y[1])>max(y[2],y[3]))
{
ll tmp1=(x[1]-x[0]+1)*(y[1]-y[0]+1);
ll tmp2=(x[3]-x[2]+1)*(y[3]-y[2]+1);
pll ans1=cal2(x[0],y[0],x[1],y[1]);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans1=cal2(x[2],y[2],x[3],y[3]);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans.first+=tmp1;
ans.second+=tmp2;
printf("%lld %lld\n",ans.first,ans.second);
}
else
{
ll tmp1=(x[1]-x[0]+1)*(y[1]-y[0]+1);
ll tmp2=(x[3]-x[2]+1)*(y[3]-y[2]+1);
pll ans1=cal2(x[0],y[0],x[1],y[1]);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans1=cal2(x[2],y[2],x[3],y[3]);
ans.first-=ans1.first;
ans.second-=ans1.second;
ans.first+=tmp1;
ans.second+=tmp2;
sort(x,x+4);
sort(y,y+4);
ans1=cal2(x[1],y[1],x[2],y[2]);
ans.first+=ans1.first;
ans.second+=ans1.second;
ll tmp3=(x[2]-x[1]+1)*(y[2]-y[1]+1);
ans.first-=tmp3;
printf("%lld %lld\n",ans.first,ans.second);
}
}
return 0;
}
D. Olya and magical square
题意
给你一个 2 n ∗ 2 n 2^{n}*2^{n} 2n∗2n的方块,每一次选一个正方形把他分成四块,一共k步问最后是否可以让最后的左下角方块的大小等于右上角方块的大小,而且可以从左下角沿着同样大小的方块走到右上角
做法
最后的路径一定可以是沿着边界走到右上角,只要路径上大小固定,就可以算出其它块可以分裂的次数,所以只要动态维护当前分裂次数a和可以分裂的次数b,如果当前分裂次数已经>k则不满足,否则如果出现a<=k&&a+b>=k说明有满足情况的答案。
动态维护分裂的过程需要维护好多个变量,首先要维护路径的长度,这样也就知道去除路径剩多少个可分裂的块。还要维护左下角块的大小,也就知道可分裂次数。之后动态维护a,b就可以了。
代码
#include<stdio.h>
typedef long long ll;
int main()
{
int t;
ll n,k;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&k);
ll a=1,b=0;// a 已用次数,b 可分解次数
ll tt=n-1;//左下角块的大小
ll lu=3;//路径上块数
int flag=0;
int ff=0;
ll cnt=0;
ll tm=1;
for(int i=0;i<=n-2;i++)
{
cnt+=tm;
b+=tm;
if(cnt>=k)
{
ff=1;
break;
}
tm=tm*4;
}
while(true)
{
if(tt==-1) break;
if(ff==1||a>k) break;
if(a<=k&&a+b>=k)
{
ff=1;
break;
}
a+=lu;
ll tmp=lu*4;//tmp 是去掉路径上的点余下的块
tt--;
lu=2LL*lu+1;
tmp-=lu;
tm=tm/4;
cnt-=tm;
b+=cnt*tmp;
}
if(tt>=0&&ff==1) printf("YES %lld\n",tt);
else printf("NO\n");
}
return 0;
}