目录
A | B | C | D | E | F |
---|---|---|---|---|---|
√ | √ | √ | √ | ○ | ○ |
( √:做出; ●:尝试未做出; ○:已补题 )
题目地址:https://codeforces.com/contest/1358
这次是疫情主题。
A Park Lighting
题意:
思路:
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int t=read();
while(t--)
{
int n=read(),m=read();
if(n>m) swap(n,m);
if(m%2==0) printf("%d\n",m/2*n);
else printf("%d\n",m/2*n+(n+1)/2);
}
return 0;
}
B Maria Breaks the Self-isolation
题意:
思路:
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e5+5;
int t,n,a[maxn];
int main()
{
//freopen("input.txt","r",stdin);
t=read();
while(t--)
{
n=read();
REP(i,1,n) a[i]=read();
sort(a+1,a+n+1);
int u=0;
REP(i,1,n) if(a[i]<=i) u=i;
printf("%d\n",u+1);
}
return 0;
}
C Celex Update
题意:
思路:可以发现答案仅跟给出的长方形的形状有关,和位置无关,就很容易找出规律。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
LL x1=read(),y1=read(),x2=read(),y2=read();
LL n=abs(x2-x1),m=abs(y2-y1);
if(n>m) swap(n,m);
LL ans=n*(n-1)+n*(m-n+1);
printf("%lld\n",ans+1);
}
return 0;
}
D The Best Vacation
题意:一年中有 n 个月,每个月有 d i d_i di 天,在每个月的 c 号出行的收益为 c,可以连续出行 x 天,问最大收益为多少。
思路:如果不考虑月份边界,那么出行时间往后挪,直到左边界或者右边界碰到了月份边界,收益都是单调变化的,所以可见出行时间的左右边界在月份边界的时候是分割点,所以只用在这个时候更新答案即可。
当时比赛的时候想得不是很清楚,导致代码异常复杂,东拼西凑,勉强过了。
题解给出的方法是,最优出行的右边界一定是月份的末尾,否则的话,向左平移一位一定会导致答案更差(其实更一开始分析的差不多,因为是单调的),所以只用枚举右边界,左边界用二分查找就可以了。这样的复杂度是 O(nlogn),我这种是 O(n)(但是写得太复杂了…)
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=4e5+5;
int n,d[maxn];
LL x,s;
LL sum(int i,int n)
{
return 1ll*i*n+1ll*n*(n-1)/2;
}
int main()
{
//freopen("input.txt","r",stdin);
cin>>n>>x;
int ld=1,l=1,rd,r;
REP(i,1,n) d[i]=read();
REP(i,1,n)
{
if(s+d[i]<x) s+=d[i];
else {rd=i,r=(int)(x-s); break;}
}
int rrd=rd,rr=r;
REP(i,n+1,n*2) d[i]=d[i-n];
n<<=1;
LL up=0,maxx=0;
if(x==1)
{
REP(i,1,n) if(d[i]>maxx) maxx=d[i];
return printf("%lld\n",maxx),0;
}
while(1)
{
//cout<<up<<endl;
//cout<<ld<<' '<<l<<' '<<rd<<' '<<r<<endl;
if(rd>n) break;
if(ld==rd)
{
up+=sum(d[ld]-r+l,r-l+1)-sum(l,r-l+1);
maxx=max(maxx,up);
up-=d[ld]-r+l;
up+=1;
l=d[ld]-r+l+1;
r=1; rd++;
}
else
{
if(d[ld]-l==d[rd]-r)
{
up-=sum(l,d[ld]-l+1);
up+=sum(r+1,d[rd]-r)+1;
maxx=max(maxx,up-1+d[ld]);
ld++, rd++, l=r=1;
}
else if(d[ld]-l<d[rd]-r)
{
up-=sum(l,d[ld]-l+1);
up+=sum(r+1,d[ld]-l+1);
maxx=max(maxx,up);
r+=d[ld]-l+1;
ld++, l=1;
}
else
{
up-=sum(l,d[rd]-r+1);
up+=sum(r+1,d[rd]-r)+1;
maxx=max(maxx,up-1+l+d[rd]-r);
l+=d[rd]-r+1;
rd++, r=1;
}
}
}
//cout<<maxx;
LL ans=0;
REP(i,1,rrd-1) ans+=sum(1,d[i]);
ans+=sum(1,rr)+maxx;
cout<<ans;
return 0;
}
E Are You Fired?
题意:一个数列长度为 n,后面 ⌊ n 2 ⌋ \lfloor \frac{n}{2}\rfloor ⌊2n⌋ 的所有元素都相同,问是否存在一个 k,使得这个数列所有长为 k 的连续子列的和都严格大于 0 。
思路:比赛的时候一直在想,这个元素相同这个条件究竟有什么作用,我推测出这个条件暗示 k 会大于等于 n/2,不过后面就没思路了。
我的猜测是正确的。假设 s i = a i + . . . + a i + k − 1 s_i=a_i+...+a_{i+k-1} si=ai+...+ai+k−1 ,如果存在一个 k 可行,那么把 k 翻倍之后,因为 s i ′ = s i + s i + k > 0 s_i^{'}=s_i+s_{i+k}>0 si′=si+si+k>0 ,所以也成立。注意到 s i + 1 = s i + ( a i + k − a i ) = s i + x − a i s_{i+1}=s_i+(a_{i+k}-a_i)=s_i+x-a_i si+1=si+(ai+k−ai)=si+x−ai,可以推出 s i + 1 = s 1 + i ⋅ x − ∑ j = 1 i a j s_{i+1}=s_1+i\cdot x-\sum\limits_{j=1}^i a_j si+1=s1+i⋅x−j=1∑iaj ,其中设 b i = i ⋅ x − ∑ j = 1 i a j b_i=i\cdot x-\sum\limits_{j=1}^i a_j bi=i⋅x−j=1∑iaj ,则 s i + 1 = s 1 + b i s_{i+1}=s_1+b_i si+1=s1+bi,可以发现 b i b_i bi 的取值跟 k 没有关系,所以看某个 k 是否可行,就是看 min 0 ≤ i ≤ n − k { s 1 + b i } > 0 \min\limits_{0\le i\le n-k} \{s_1+b_i\}>0 0≤i≤n−kmin{s1+bi}>0 是否成立。所以一开始先维护一个 b 的前缀最小值,然后枚举 k 判断即可。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=5e5+5;
LL a[maxn],b[maxn],x,s,s1;
int main()
{
//freopen("input.txt","r",stdin);
int n=read(),ans=0;
REP(i,1,(n+1)/2) a[i]=read();
x=read();
REP(i,(n+1)/2+1,n) a[i]=x;
REP(i,1,n)
{
s+=a[i];
b[i]=min(b[i-1],x*i-s);
}
REP(i,1,n/2) s1+=a[i];
REP(k,n/2+1,n)
{
s1+=a[k];
if(s1+b[n-k]>0)
{
ans=k;
break;
}
}
printf("%d\n",ans?ans:-1);
return 0;
}
F Tasty Cookie
题意:两个长度为 n( 1 ≤ n ≤ 2 e 5 1\le n \le 2e5 1≤n≤2e5)的数组 A 和 B( 1 ≤ A i , B i ≤ 1 0 12 1 \le A_i,B_i\le 10^{12} 1≤Ai,Bi≤1012),可以对 A 进行两种操作:1.翻转;2.求前缀和。问能否通过若干次操作,使得 A 变为 B,如果可以并且操作次数小于等于 2e5,还要输出操作方案。
思路:比赛的时候根本没看这道题。
首先根据数据限制,我们可以思考最多能求多少次前缀和,然后发现当 n ≤ 2 n\le 2 n≤2 的时候次数可以非常多,当 n > 2 n>2 n>2 时就可以暴力处理,因为翻转一定不会比求前缀和多多少。
如果 n = 1 n=1 n=1 ,那就是一个判断是否相等的问题;
否则,我们考察一个前缀数组,在题目数据限制下,这个前缀数组一定是严格单增的,所以可以用 B 数组反推回去:
-
如果 n = 2 n=2 n=2 ,这里可以通过取模运算加速第二种操作,这种情况要注意很多细节问题;
-
如果 n > 2 n>2 n>2 ,那么就循环处理,每次判断是否和 A 或者 A 的翻转相等,不相等的话就判断是否单增或者单减,如果单调则继续处理(差分),否则就 IMPOSSIBLE。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const LL maxn=2e5;
int n,tot;
LL a[maxn+5],b[maxn+5];
char s[maxn*10];
bool equalzheng()
{
REP(i,1,n) if(a[i]!=b[i]) return 0;
return 1;
}
bool equalfan()
{
REP(i,1,n) if(a[i]!=b[n-i+1]) return 0;
return 1;
}
bool danzeng()
{
REP(i,1,n-1) if(b[i+1]<=b[i]) return 0;
return 1;
}
bool danjian()
{
REP(i,1,n-1) if(b[i+1]>=b[i]) return 0;
return 1;
}
void backb()
{
REP_(i,n,2) b[i]-=b[i-1];
}
int main()
{
//freopen("input.txt","r",stdin);
n=read();
REP(i,1,n) scanf("%lld",&a[i]);
REP(i,1,n) scanf("%lld",&b[i]);
if(n==1)
{
if(a[1]==b[1]) printf("SMALL\n0");
else puts("IMPOSSIBLE");
}
else if(n==2)
{
LL ans=0;
while(!equalzheng())
{
if(b[1]>b[2])
{
swap(b[1],b[2]);
if(ans<maxn) s[++tot]='R';
}
if(equalfan()) {s[++tot]='R'; break;}
if(b[1]==min(a[1],a[2]))
{
if(b[2]<max(a[1],a[2]) || (b[2]-max(a[1],a[2]))%b[1]!=0)
return puts("IMPOSSIBLE"),0;
LL x=(b[2]-max(a[1],a[2]))/b[1];
ans+=x;
if(ans<=maxn) while(x--) s[++tot]='P';
if(b[1]!=a[1]) s[++tot]='R';
break;
}
if(b[1]<a[1] && b[1]<a[2]) return puts("IMPOSSIBLE"),0;
LL x=(b[2]-b[1])/b[1]+1;
ans+=x;
if(ans<=maxn) while(x--) s[++tot]='P';
b[2]%=b[1];
}
if(ans<=maxn)
{
printf("SMALL\n%d\n",tot);
REP_(i,tot,1) putchar(s[i]);
}
else printf("BIG\n%lld",ans);
}
else
{
LL ans=0;
while(1)
{
if(equalzheng()) break;
if(equalfan()) {if(ans<=maxn) s[++tot]='R'; break;}
if(danzeng()) {ans++; if(ans<=maxn) s[++tot]='P'; backb();}
else if(danjian()) {if(ans<=maxn) s[++tot]='R'; reverse(b+1,b+n+1);}
else return puts("IMPOSSIBLE"),0;
}
if(ans<=maxn)
{
printf("SMALL\n%d\n",tot);
REP_(i,tot,1) putchar(s[i]);
}
else printf("BIG\n%lld",ans);
}
return 0;
}