AtCoder Beginner Contest 355
ABC355 A - Who Ate the Cake?
代码(签到题)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
int n=iut(),m=iut();
if (n==m) printf("-1");
else printf("%d",6-n-m);
return 0;
}
ABC355 B - Piano 2
代码(签到题)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,a[211],b[211];
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=iut(),m=iut();
for (int i=1;i<=n;++i) a[i]=iut();
for (int i=1;i<=m;++i) b[i]=iut();
sort(a+1,a+1+n),sort(b+1,b+1+m);
int I=1,J=1,flag=0;
while (I<=n&&J<=m)
if (a[I]<b[J]){
if (flag) return !printf("Yes");
flag=1,++I;
}else ++J,flag=0;
if (I<n) return !printf("Yes");
else printf("No");
return 0;
}
ABC355 C - Bingo 2
代码(签到题)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2011;
int n,T,row[N],column[N],zhu,fu;
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=iut(),T=iut();
for (int i=1;i<=T;++i){
int t=iut(),x=(t-1)/n+1,y=t-(x-1)*n;
++row[x],++column[y];
if (x==y) ++zhu;
if (x+y==n+1) ++fu;
if (row[x]==n||column[y]==n||zhu==n||fu==n)
return !printf("%d",i);
}
return !printf("-1");
}
ABC355 D - Intersecting Intervals
分析
考虑容斥,用总数减去不相交的区间数,不相交的区间数可以用树状数组实现
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1000011;
long long ans;
int n,m,l[N],r[N],b[N],c[2][N];
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void update(int x,int z){
for (;x<=m;x+=-x&x) ++c[z][x];
}
int query(int x,int z){
int ans=0;
for (;x;x-=-x&x) ans+=c[z][x];
return ans;
}
int main(){
n=iut();
for (int i=1;i<=n;++i){
l[i]=iut(),r[i]=iut();
b[++m]=l[i],b[++m]=r[i];
}
sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1;
ans=n*(n-1ll)/2;
for (int i=1;i<=n;++i){
l[i]=lower_bound(b+1,b+1+m,l[i])-b;
r[i]=lower_bound(b+1,b+1+m,r[i])-b;
ans-=query(l[i]-1,0)+i-1-query(r[i],1);
update(r[i],0),update(l[i],1);
}
return !printf("%lld",ans);
}
ABC355 E - Guess the Sum
分析
实际上,连续和的求值可以变成两个前缀和相减的形式,而 \((L,R)\) 区间最多只有 \(n2^n\) 个,
要求询问次数最少,询问实际上只与端点有关,询问次数实际上是最短路,那么使用 BFS 找到最短路的路径,利用路径完成交互。
代码
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N=300011; queue<int>q;
int pre[N],lg[N],n,L,R,ans,z;
void doit(int x){
if (pre[x]==pre[L]) return;
if (pre[x]<x) cout<<"? "<<lg[x-pre[x]]<<' '<<pre[x]/(x-pre[x])<<endl,cin>>z,ans=(ans+z)%100;
else cout<<"? "<<lg[pre[x]-x]<<' '<<x/(pre[x]-x)<<endl,cin>>z,ans=(ans+100-z)%100;
doit(pre[x]);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>L>>R,++R,lg[0]=pre[0]=-1;
for (int i=1;i<=(1<<n);++i) pre[i]=-1,lg[i]=lg[i>>1]+1;
pre[L]=(1<<n)+1,q.push(L);
while (!q.empty()){
int x=q.front(); q.pop();
if (x==R){
doit(R);
cout<<"! "<<ans<<endl;
return 0;
}
if (!x){
for (int z=1;z<pre[L];z<<=1)
if (pre[z]==-1) q.push(z),pre[z]=x;
continue;
}
for (int z=-x&x;z;z>>=1){
if (x+z<pre[L]&&pre[x+z]==-1) q.push(x+z),pre[x+z]=x;
if (pre[x-z]==-1) q.push(x-z),pre[x-z]=x;
}
}
return 0;
}
ABC355 F - MST Query
分析
边权最多 \(10\) 种,一开始是一棵树,那么一开始的答案就是所有边权之和,
那么开 \(10\) 个并查集,第 \(i\) 个并查集中只装入边权大于等于 \(i\) 的边,
对于每个询问,将边丢入相应的并查集内,如果该边构成了新的生成树,说明原边被替换了,
假设原边的边权为 \(x\),新边的边权为 \(y\),那么在第 \(y\sim x-1\) 个并查集中均添加了新边,
减去 \(x-y\) 的贡献,所以就是添加一个新边,就将答案减一。
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200011;
int n,Q,f[10][N],ans;
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
int getf(int j,int u){return f[j][u]==u?u:f[j][u]=getf(j,f[j][u]);}
int main(){
n=iut(),Q=iut();
for (int j=1;j<10;++j)
for (int i=1;i<=n;++i) f[j][i]=i;
for (int i=1;i<n;++i){
int x=iut(),y=iut(),z=iut();
ans+=z;
for (int j=z;j<10;++j)
f[j][getf(j,x)]=f[j][getf(j,y)];
}
for (int i=1;i<=Q;++i){
int x=iut(),y=iut(),z=iut();
for (int j=z;j<10;++j)
if (getf(j,x)!=getf(j,y))
f[j][getf(j,x)]=f[j][getf(j,y)],--ans;
else break;
print(ans),putchar(10);
}
return 0;
}
ABC355 G - Baseball
分析
乘上了 \(\sum_{i=1}^nP_i\),不妨令第 \(0\) 个位置和第 \(n+1\) 个位置必选但在最小值时不可用。
那么题目就转换成了恰好选择 \(k\) 个点分成 \(k+1\) 段,对于相邻的选择点 \(x,y\),使得 \(\sum\sum_{i=x+1}^{y-1}\min\{i-x,y-i\}\times P_i\) 最小
设 \(dp[i][j]\) 表示前 \(i\) 个位置分成了 \(j\) 段的最小值,第二维由于式子满足四边形不等式,可利用决策单调性进行优化。
即使优化后仍是于事无补,考虑利用 WQS 二分斜率强加贡献,那么就变成了 \(O(n\log n\log k)\),貌似斜率的二分上界要上到 long long
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=50011; int n,m,head,tail,K[N],a[N],f[N],q[N];
typedef long long lll; lll dp[N],s[N],S[N],ans;
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
lll calc(int l,int r){
if (l>r) return 0;
if (l==1&&r==n) return 1e12;
if (l==1) return (s[r]-s[l-1])*(r+1)-(S[r]-S[l-1]);
if (r==n) return (S[r]-S[l-1])-(s[r]-s[l-1])*(l-1);
int mid=(l+r)>>1;
return (S[mid]-S[l-1])-(s[mid]-s[l-1])*(l-1)+(s[r]-s[mid])*(r+1)-(S[r]-S[mid]);
}
int ef(int X,int Y){
int l=Y+1,r=n+2;
while (l<r){
int mid=(l+r)>>1;
if (dp[X]+calc(X+1,mid-1)>=dp[Y]+calc(Y+1,mid-1)) r=mid;
else l=mid+1;
}
return l;
}
void doit(lll mid){
head=tail=0;
for (int i=1;i<=n+1;++i){
while (head<tail&&K[head]<=i) ++head;
dp[i]=dp[q[head]]+calc(q[head]+1,i-1)+mid,f[i]=f[q[head]]+1;
while (head<tail&&K[tail-1]>=ef(q[tail],i)) --tail;
K[tail]=ef(q[tail],i),q[++tail]=i;
}
}
int main(){
n=iut(); m=iut()+1;
for (int i=1;i<=n;++i) a[i]=iut();
for (int i=1;i<=n;++i) s[i]=s[i-1]+a[i],S[i]=S[i-1]+1ll*a[i]*i;
lll l=0,r=623623623617ll;
while (l<r){
lll mid=(l+r+1)>>1; doit(mid);
if (f[n+1]>=m) ans=dp[n+1]-m*mid,l=mid;
else r=mid-1;
}
return !printf("%lld",ans);
}