Codeforces Round #524 (Div. 2)
虽然并没有打qwq
但是感觉打了我就能上分了(虽然还是蓝名)
A
题意
要制作n个邀请函,每个邀请函要花费红纸\(*2\),绿纸\(*5\),蓝纸\(*8\),同时商店里有若干有k张相同颜色纸的本子,问做完这些邀请函最少要几个这样的本子
题解
普及T1,输入一行输出一行,应该都会吧qwq
#define LL long long
LL n=rd(),k=rd();
cout<<(n*2+k-1)/k+(n*5+k-1)/k+(n*8+k-1)/k<<endl;
B
题意
有一个序列\(a\),其中\(a_1=-1,a_2=2,a_3=-3....a_n=n*(-1)^n\),每次询问某段区间之和
题解
求区间和考虑前缀和,容易发现前缀和\(pre_i=\lceil\frac{i}{2}\rceil*(-1)^i\),所以可以O(1)求,然后输出\(pre_r-pre_{l-1}\)
#include<bits/stdc++.h>
#define il inline
#define LL long long
using namespace std;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int main()
{
int T=rd();
while(T--)
{
int l=rd()-1,r=rd();
l=(l+1)/2*((l&1)?-1:1),r=(r+1)/2*((r&1)?-1:1);
printf("%d\n",r-l);
}
return 0;
}
C
题意
有一个\(n\)行\(m\)列的黑白相间矩阵,其中第一行第一列为白色,现在先把一个子矩阵染成白色,然后再把另一个子矩阵染成黑色,问最后总共有几个白格几个黑格
题解
首先考虑如果要统计初始情况下某个大小为\(r*c\)的子矩阵中的黑白格子数目怎么做,可以发现如果左下角为白格,那么白格有\(\lceil\frac{r*c}{2}\rceil\)个,黑格有\(\lfloor\frac{r*c}{2}\rfloor\)个,如果左下角为黑格,那么白格有\(\lfloor\frac{r*c}{2}\rfloor\)个,黑格有\(\lceil\frac{r*c}{2}\rceil\)个
现在只考虑白格的变化,(因为黑格=总数-白格),先把一个子矩形染白,那么白色格子数目就加上那个子矩形内黑格的数目,把一个子矩形染黑,按道理来说白格子应该是减少那个子矩形内白格的数目,但是如果两个子矩形有交就不对了,所以如果有交,那么白格子数目要减去那个子矩形内白格的数目,再加上相交的部分的白格数目减去相交的部分格子总数就是减去相交的部分黑格数目
#include<bits/stdc++.h>
#define il inline
#define re register
#define LL long long
#define db double
#define eps (1e-5)
using namespace std;
const int N=10000+10,M=100+10,P=(1<<20)+10;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
LL n,m,a[4],b[4],a1,a2,dt;
il LL calw(LL lx,LL ly,LL rx,LL ry){return (rx-lx+1)*(ry-ly+1)/2+(((rx-lx+1)*(ry-ly+1))&(lx+ly+1)&1);}
il LL calb(LL lx,LL ly,LL rx,LL ry){return (rx-lx+1)*(ry-ly+1)/2+(((rx-lx+1)*(ry-ly+1))&(lx+ly+0)&1);}
int main()
{
int T=rd();
while(T--)
{
n=rd(),m=rd();
for(int i=0;i<4;i++) a[i]=rd();
for(int i=0;i<4;i++) b[i]=rd();
a1=calw(1,1,n,m)-calw(b[0],b[1],b[2],b[3])+calb(a[0],a[1],a[2],a[3]),a2=calb(1,1,n,m)-calb(a[0],a[1],a[2],a[3])+calw(b[0],b[1],b[2],b[3]);
if(max(a[0],b[0])<=min(a[2],b[2])&&max(a[1],b[1])<=min(a[3],b[3]))
{
a[0]=max(a[0],b[0]),a[1]=max(a[1],b[1]),a[2]=min(a[2],b[2]),a[3]=min(a[3],b[3]);
dt=-calw(a[0],a[1],a[2],a[3])+(a[2]-a[0]+1)*(a[3]-a[1]+1);
a1-=dt,a2+=dt;
}
printf("%lld %lld\n",a1,a2);
}
return 0;
}
D
题意
初始有一个边长为\(2^n\)的正方形,要进行\(k\)次分裂操作,其中每次操作是把一个边长为\(2^a(a\ge1)\)的正方形横一刀竖一刀,变成四个\(2^{a-1}\)的正方形,问能否通过\(k\)次操作,使得最左边一排和最上面一排的小正方形边长相等
题解
如果\(k=0\),直接输出\(YES\)和\(n\)
否则我们是现需要用一些操作使得最左边一排和最上面一排的小正方形边长相等,然后把剩下的操作用在其他的正方形上浪费掉.开两个变量,分别记录最左边一排和最上面一排的小正方形个数\(a\)和其余没用正方形能被分割多少次\(b\),每次分割,都会把\(a\)个边长为\(2^m\)正方形分成\(2a+1\)个边长为\(2^{m-1}\)有用正方形和\(2a-1\)个边长为\(2^{m-1}\)无用正方形,单个边长为\(2^m\)无用正方形会给\(b\)加上\(\sum_{i=0}^{m-1}4^i\),所以每次操作b会加上\((2a-1)\sum_{i=0}^{m-2}4^i\).
在某一时刻如果剩余次数\(\le b\)就输出\(YES\)和当前的\(n\),如果一直不行就输出\(NO\)
#include<bits/stdc++.h>
#define il inline
#define re register
#define LL long long
#define ull unsigned long long
#define db double
#define eps (1e-5)
using namespace std;
//const int N=250+10;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
LL n,m,k,s[40],a,tt;
il LL S(LL p){return s[min(p,m)];}
il LL mul(LL a,LL b){return sqrt(a)*sqrt(b)<=sqrt(2)*1e9?a*b:1e18;}
il LL add(LL a,LL b){return min(a+b,(LL)(1e18));}
int main()
{
///aha
s[1]=1;
for(m=2;m<=40;m++)
{
s[m]=s[m-1]*4+1;
if(s[m]>=1e18) {s[m]=1e18;break;}
}
++s[m];
int T=rd();
while(T--)
{
n=rd(),k=rd();
if(!k) {cout<<"YES "<<n<<endl;continue;}
tt=0,a=1;
for(;n&&(~tt);)
{
k-=a;
tt=add(tt,mul(S(n-1),a*2-1));
--n,a=a*2+1;
if(k<=tt) {cout<<"YES "<<n<<endl,tt=-1;}
if(k<a) break;
}
if(~tt) puts("NO");
}
return 0;
}
E
题意
给一个\(n\)行\(m\)列的由小写字母组成的矩阵,问有多少个子矩阵,这个子矩阵内通过每行之中的字母交换顺序,使得这个子矩阵每行和每列都是回文串
题解
如果这个子矩阵只有一行,那么这一行能变成回文串当且仅当所有字母中只有不超过1种字母出现次数为奇数.那么如果某个\(r\)行的子矩阵是符合要求的,那么第\(i\)行和第\(r-i+1\)行所有字母出现个数要相同.所以可以枚举两列,然后对每一行的字母出现次数\(hash\)起来,然后就是一个求某个串内的回文子串个数问题,\(manacher\)即可
注意如果某一行不能构成回文串,那么不计入\(manacher\)的统计范围,也不计入答案
#include<bits/stdc++.h>
#define il inline
#define re register
#define LL long long
#define ull unsigned long long
#define db double
#define eps (1e-5)
using namespace std;
const int N=250+10;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int n,m,len,a[N][N][26],p[N<<1],oi,ans;
ull ha[N<<1];
bool ok[N<<1];
int main()
{
n=rd(),m=rd();
len=(n<<1)+3;
char cc[N];
for(int i=1;i<=n;i++)
{
scanf("%s",cc+1);
for(int j=1;j<=m;j++)
{
for(int k=0;k<26;k++) a[i][j][k]=a[i][j-1][k];
++a[i][j][cc[j]-'a'];
}
}
ha[0]-=2,ha[len-1]-=3;
for(int i=1;i<=n+1;i++) --ha[i*2-1];
memset(ok,1,sizeof(ok));
ok[0]=ok[len-1]=0;
for(int ll=1;ll<=m;ll++)
for(int rr=ll;rr<=m;rr++)
{
for(int i=1;i<=n;i++)
{
ha[i*2]=0;
int cn=0;
for(int k=0;k<26;k++)
{
int xx=a[i][rr][k]-a[i][ll-1][k];
ha[i*2]=ha[i*2]*377+xx,cn+=xx&1;
}
ok[i*2]=(cn<=1);
}
memset(p,0,sizeof(p)),oi=0;
for(int i=1;i<len;i++)
{
if(ok[i])
{
if(i<=oi+p[oi]) p[i]=min(oi+p[oi]-i,p[oi*2-i]);
while(ha[i-p[i]-1]==ha[i+p[i]+1]&&ok[i-p[i]-1]&&ok[i+p[i]+1]) ++p[i];
if(i+p[i]>=oi+p[oi]) oi=i;
}
ans+=(p[i]+1)>>1;
}
}
printf("%d\n",ans);
//sand sculptrue problem
return 0;
}
F
题意
有\(n\)个集合和\(k\)条线段,其中每条线段用\((l,r,p)\)表示,意思是左端点为\(l\),右端点为\(r\),属于第\(p\)个集合,现在有\(m\)个询问,每个询问形如\((a,b,x,y)\),要求的是编号在\([a,b]\)中的所有集合是否都有一条线段包含在\([x,y]\)中,也就是\(x\le l \le r \le y\)
题解
首先考虑暴力,把所有线段按左端点升序排序,然后每次找到最靠前的,左端点\(\ge x\)的线段,然后往后扫一遍,看每个集合是否都有一条线段右端点\(\le y\)
如果询问中\(a\)不变,那么可以对每个集合记一个值\(w\),表示集合中左端点\(\ge x\)的线段中右端点最小值,每次询问就是在某个区间\([a,b]\)中看\(w\)最大值是否\(\le y\)
不过\(a\)是可以变的,所以考虑从右往左把线段插入主席树中,每次询问二分查找最靠左的,且代表的线段左端点\(\ge x\)的线段树版本,然后在这个版本上做上述操作就好了
#include<bits/stdc++.h>
#define il inline
#define re register
#define LL long long
#define ull unsigned long long
#define db double
#define eps (1e-5)
using namespace std;
const int N=300000+10,M=100000+10;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int n,m,k;
struct node
{
int l,r,n;
bool operator < (const node &bb) const {return l<bb.l;}
}p[N];
int s[N*20],ch[N*20][2],rt[N],tt;
int st[110],tp;
int quer(int o,int l,int r,int ll,int rr)
{
if(!o) return 1<<30;
if(ll<=l&&r<=rr) return s[o];
int mid=(l+r)>>1,an=0;
if(ll<=mid) an=max(an,quer(ch[o][0],l,mid,ll,rr));
if(rr>mid) an=max(an,quer(ch[o][1],mid+1,r,ll,rr));
return an;
}
il void inst(int o1,int o2,int x,int y)
{
y=min(y,quer(o2,1,n,x,x));
int l=1,r=n;
while(l<r)
{
st[++tp]=o1;
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++tt,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++tt;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
}
s[o1]=y;
while(tp)
{
o1=st[tp--];
s[o1]=max(s[ch[o1][0]],s[ch[o1][1]]);
}
}
int main()
{
//技不如人qwq
n=rd(),m=rd(),k=rd();
for(int i=1;i<=k;++i) p[i].l=rd(),p[i].r=rd(),p[i].n=rd();
sort(p+1,p+k+1);
s[0]=1<<30;
for(int i=k;i>=1;--i)
inst(rt[i]=++tt,rt[i+1],p[i].n,p[i].r);
while(m--)
{
int a=rd(),b=rd(),x=rd(),y=rd();
int l=1,r=k,z=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(p[mid].l>=x) z=mid,r=mid-1;
else l=mid+1;
}
if(!z) puts("no");
else puts(quer(rt[z],1,n,a,b)<=y?"yes":"no");
cout.flush();
}
return 0;
}