AtCoder Beginner Contest 418 C-E 题解
这又是目前所有 abc\text{abc}abc 打得最好的一次:上青了QwQ!!!!
A B 都是 SB 题,不放了
C. Flush
这个标题让人想到Flush after use(上完WC记得冲水)
题意
有 NNN 种不同口味的茶包,第 iii 种有 AiA_iAi 包。
对于第 iii 轮游戏,游戏难度为 bbb,此时,你可以选择一个整数 x(b≤x≤∑Ai)x(b\le x\le \sum A_i)x(b≤x≤∑Ai),使得从所有茶包中任意选择 xxx 个茶包时,都存在至少存在 bbb 个相同味道的茶包,请找到每一轮的最小整数 xxx,若无法选择一个 xxx,输出 -1
。
思路
由于味道和顺序无关,所以可以先把 AAA 按升序排序。
我们新定义一个数组 Ci=min(Ai,b)C_i=\min(A_i,b)Ci=min(Ai,b),可以发现 CCC 的前半部分都是 AiA_iAi,后半部分都等于 b−1b-1b−1。我们把第一个 Ci=b−1C_i=b-1Ci=b−1 的 iii 记作 pospospos。
此时,我可以告诉你结论,x=(∑Ci)+1x=(\sum C_i)+1x=(∑Ci)+1。为什么呢?
因为只要全按 CiC_iCi 选满时,再选一个一定能使得一个 Cj=bC_j= bCj=b。
所以,先排序,然后用前缀和记录: Sumi=∑j=1iAiSum_i=\sum_{j=1}^i A_iSumi=∑j=1iAi 考虑二分实现,对于每个 BiB_iBi,二分出第一个 jjj 使得 Aj≥BiA_j\ge B_iAj≥Bi,则本次答案为 x=Sumj−1+(n−j+1)×(Bi−1)+1x= Sum_{j-1}+(n-j+1)\times (B_i-1)+1x=Sumj−1+(n−j+1)×(Bi−1)+1。
C++ 代码
Submission #68325834
#include<bits/stdc++.h>
#define int long long
const int maxn=300005;
int v[maxn];
int sum[maxn];
signed main(){int n,q;cin>>n>>q;for(int i=1;i<=n;i++) cin>>v[i];sort(v+1,v+1+n);for(int i=1;i<=n;i++) sum[i]=sum[i-1]+v[i];while(q--){int x; cin>>x;if(v[n]<x){cout<<-1<<endl;continue;}int p=lower_bound(v+1,v+1+n,x)-v-1;int ans=sum[p]+(n-p)*(x-1)+1;cout<<ans<<endl;}return 0;
}
D. XNOR Operation
题意
有一个 01\texttt{01}01 字符串 TTT。一个字符串 SSS 是美丽的,当且仅当经过若干次以下操作后,S=1S=\texttt 1S=1:
- 操作:选择一个 i(1≤i<∣S∣)i(1\le i <|S|)i(1≤i<∣S∣),将 SiS_iSi 和 Si+1S_{i+1}Si+1 替换为 ¬(si⊕si+1)\neg (s_i \oplus s_{i+1})¬(si⊕si+1),(此处 si,si+1s_i,s_{i+1}si,si+1 代表SiS_iSi 和 Si+1S_{i+1}Si+1 对应的数字)。
找到 TTT 的美丽的非空子串(连续)的数量。
思路
对于字符串 000,001,010,011,100,101,110,111\texttt{000},\texttt{001},\texttt{010},\texttt{011},\texttt{100},\texttt{101},\texttt{110},\texttt{111}000,001,010,011,100,101,110,111,枚举一下,发现先算前面和先算后面结果是一样的,也就是说这个操作是满足结合率的。
所以,对于每个前缀异或非和 SumiSum_iSumi,找到满足 j<ij<ij<i 且 Sumj=SumiSum_j=Sum_iSumj=Sumi 的 jjj 的数量即可。特别地,Sum0=0Sum_0=0Sum0=0。
这个过程可以动态用 map
维护。
C++ 代码
Submission #68333224
#include<bits/stdc++.h>
#define int long long
const int maxn=200005;
int sum[maxn];
signed main(){int n;string s;cin>>n>>s;s=" "+s;for(int i=1;i<=n;i++){if(s[i]=='0'&&sum[i-1]==1) sum[i]=0;else if(s[i]=='0'&&sum[i-1]==0) sum[i]=1;else sum[i]=sum[i-1];}map<int,int> mp;mp[0]=1;int ans=0;for(int i=1;i<=n;i++){ans+=mp[sum[i]];mp[sum[i]]++;}cout<<ans<<endl;return 0;
}
E. Trapezium
题意
有 NNN 个点,第 iii 个点坐标为 (Xi,Yi)(X_i,Y_i)(Xi,Yi),保证两两坐标不同,且任意三点不共线。
找到梯形(一组对边平行,另一组对边可平行可不平行)的个数。
思路
由于 N≤2000N \le 2000N≤2000,可以考虑 n2n^2n2 级别的做法。
可以想到的是如果两条边平行,那么这两条直线的斜率 k(k=ΔyΔx)k(k=\dfrac{\Delta y}{\Delta x})k(k=ΔxΔy) 就相同。
所以,可以枚举两个点 (Xi,Yi),(Xj,Yj)(X_i,Y_i),(X_j,Y_j)(Xi,Yi),(Xj,Yj),其斜率为 k=Yi−YjXi−Xjk=\dfrac{Y_i-Y_j}{X_i-X_j}k=Xi−XjYi−Yj ,用两个数 dx,dydx,dydx,dy 表示这个斜率的最简分数 dydx\dfrac{dy}{dx}dxdy,然后,用一个 map<pair<int,int>,int>
来记录对于斜率为 dydx\dfrac{dy}{dx}dxdy 的点的数量。并统计梯形个数。
但是,平行四边形会重复计算,所以还要减去平行四边形的个数。
这里要用到一个定理:对角线互相平分的四边形是平行四边形。
所以还是枚举两个点,记录它们的中点坐标 (Xi+Xj2,Yi+Yj2)(\dfrac{X_i+X_j}{2},\dfrac{Y_i+Y_j}{2})(2Xi+Xj,2Yi+Yj),再用一个 map<pair<int,int>,int>
统计数量即可。
注意常数优化。
C++ 代码
Submission #68347632
#include<bits/stdc++.h>
#define mpr make_pair
using namespace std;
typedef pair<int,int> pii;
const int maxn=2005;
const int inf=2e9;
int x[maxn],y[maxn];
int main(){int n;cin>>n;map<pii,int> mp,pa;for(int i=1;i<=n;i++) cin>>x[i]>>y[i];ll ans=0;for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){int dx=x[i]-x[j];int dy=y[i]-y[j];int gc=__gcd(dx,dy);if(gc!=0) dx/=gc,dy/=gc;else if(dy==0) dx=inf;else dy=inf;if(dx<0) dx=-dx,dy=-dy;ans+=mp[mpr(dx,dy)]++;ans-=pa[mpr(x[i]+x[j],y[i]+y[j])]++;}}cout<<ans<<endl;return 0;
}