====== Codeforces Round 645(div2) ====== ===== A ===== 题意:有一块n*m的矩阵,定义方格边界上放一盏灯可以点亮上下或者左右的格子(若放在上下边上,点亮上下两个格子,若左右两边,则左右两个格子),问要点亮所有各自需要多少盏灯? 题解:不妨枚举几种情况,发现如果总共有n*m个格子,我们一定能找到一种方法,使每一个灯正好点亮两个未点亮的区域,那么答案就显而易见了,为$\lceil\frac{nm}{2}\rceil$。代码略 ===== B ===== 题意:一个场景,A要请一系列人来♂van,每个人都有一个编号,一次可以请许多人,但是要保证每一次请一堆人过来时需要保证,每一个刚来的人的编号要比除了他之外在场的人的数量小。最初A算一个人,问最多能叫多少个人。 题解:很容易可以发现每从序列中选一个人则,总容量会+1,那么叫第$k$个人的时候,当场会有至少$k$个人在场,所以考虑最值只要考虑边界条件,我们先把序列进行排序,考虑序列中每一个满足$a_i≤i$的点,只要有该式满足可以保证前面i个数一定满足条件,所以只需要考虑每一个满足$a_i≤i$,的点并且取i的最大值即可。 #include using namespace std; const int maxn=1e5+13; int a[maxn]; int main() { int t,n,m; scanf("%d",&t); while(t--) { int ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;i++) { if(a[i]<=i) { ans=max(ans,i); } } printf("%d\n",ans+1); } } ===== C ===== 题意:有一个三角型排列为左图所示,定义一次移动只能从$(i,j)$到$(i+1,j)$或者从$(i,j)$到$(i,j+1)$,问若将该三角形阵列的$(i_1,j_1)$,点走到$(i_2,j_2)$中途经过的所有点的值全部加在一起,有多少种不同的答案?{{:2020-2021:teams:manespace:1j_p2nm_r_f_94uoq_1b_b.png?200 |}} 题解:这题真是坑死我了……,可以找规律然后暴力计算个数,因为枚举之后不难发现两点之间路径和最长一定是先向下走,再向上走,最小一定是先向右走再向下走,然后根据等差数列的关系暴力求式子计算。后来发现答案是很简单的$(i_2-i_1)*(j_2-j_1)+1$,直接自闭,后来想想发现还挺自然(知道答案当然自然了),每个数的下边一定比左边大1,加的效果是叠加的,假如这一次比上一次早一步走了下面,那么一定会给整体加上$(j_2-j_1)$,那么一共可以早$(i_2-i_1)$次。可以得到,最小的和最大的差,然后+1即可。代码略。 ===== D ===== 题意:重新定义月份的数量和每月的天数,规定每月的第$i$天有一个权值为$i$,给定月数与每个月的天数,问连续的k天的最大权值和为多少。 题解:容易证明若要最大,连续k天末尾必须要是一个月的月末,否则当我们向后移动,一定能找到更加优秀的解。那么我门枚举月份即可。注意先预处理,要不然会tle…… #include using namespace std; typedef long long ll; const int maxn=5e5+13; ll a[maxn]; int n; ll x; int main() { scanf("%d%lld",&n,&x); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); a[i+n]=a[i]; } n*=2; ll ans=0; ll t=0,f=0; int l=n/2; while(a[l]+t<=x) t+=a[l],f+=a[l]*(a[l]+1)/2,l--; for(int i=n/2+1;i<=n;i++) { t+=a[i]; f+=a[i]*(a[i]+1)/2; while(t>x) t-=a[l+1],f-=a[l+1]*(a[l+1]+1)/2,l++; ll tp=f; if(t ===== E ===== 题意:给定长度为n的序列,而且这个序列满足后$\lfloor\frac{n}{2}\rfloor$个数全部为一个值$x$,问是否存在一个整数$k$使得所有长度为$k$的子序列和一定为正数。 题解:容易证明若一个小于等于$\lfloor\frac{n}{2}\rfloor$的数$k$满足条件,则$2k$也满足条件。定义$s_i=\sum_{j=i}^{i+k-1}a_j$还有一个明显的递推式为$s_{i+1}=s_i-a_{i}+a_{i+k}$,这样我们可以预处理出一个序列$[s_i,a_{k+1}-a_1,a_{k+2}-a_2,……,a_{n}-a_{n-k}]$,发现我们需要求的$s_{i}$就是该序列的前缀和,由于$k≥\lfloor\frac{n}{2}\rfloor$,则上式可以改写为$[s_i,x-a_1,x-a_2,……,x-a_{n-k}]$,我们处理出这个式子的前缀和,然后找出最小值,这也可以预处理出来,之后为了求$s_{i}$,我们与处理出$a$的前缀和然后我们枚举$k$的情况,看是否存在$k$使所有$s$都大于0,复杂度$O(n)$。 #include using namespace std; typedef long long ll; const int maxn=6e5+13; ll a[maxn]; ll m[maxn]; ll pre[maxn]; int main() { ll n,x; ll ans=0; scanf("%lld",&n); for(int i=1;i<=(n+1)/2;i++) { scanf("%lld",&a[i]); pre[i]=pre[i-1]+a[i]; } scanf("%lld",&x); m[1]=0; ll t=0; for(int i=2;i<=(n+1)/2+1;i++) { t+=x-a[i-1]; m[i]=min(m[i-1],t); } ll test=pre[(n+1)/2]; for(int k=(n+1)/2;k<=n;k++) { if(test+m[n-k+1]>0) { ans=1ll*k; break; } test+=x; } if(ans==0) printf("-1"); else printf("%lld",ans); }