前言
非比赛题解,Div 4 感觉没什么好说的。
题目连接:Dashboard - Codeforces Round 952 (Div. 4) - Codeforces
A. Creating Words
略。
#include<cstdio>
#include<cstring>
using namespace std;
int T;
char a[5],b[5];
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%s%s",a + 1,b + 1);
char s = a[1];
a[1] = b[1],b[1] = s;
printf("%s %s\n",a + 1,b + 1);
}
return 0;
}
B. Maximum Multiple Sum
略。
#include<cstdio>
#include<cstring>
using namespace std;
int T,x,y,z;
long long k,ans;
long long max(long long x,long long y) { return x > y ? x : y ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d%d%lld",&x,&y,&z,&k),ans = 0ll;
for (int a = 1;a <= x;++ a)
if(!(k % a))
for (int b = 1;b <= y;++ b)
if(!(k % (a * b)))
{
long long c = k / (long long)(a * b);
if(!c || c > z) continue;
ans = max(ans,(long long)(x - a + 1ll) * (y - b + 1ll) * (z - c + 1ll));
}
printf("%lld\n",ans);
}
return 0;
}
C. Good Prefixes
略。
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,ans;
long long sum,mx,a;
long long max(long long x,long long y) { return x > y ? x : y ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d",&n),ans = sum = mx = 0;
for (int i = 1;i <= n;++ i)
{
scanf("%lld",&a);
sum += a;
mx = max(mx,a);
if(mx == sum - mx) ++ ans;
}
printf("%d\n",ans);
}
return 0;
}
D. Manhattan Circle
略。
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,m;
long long tot,sx,sy;
char s;
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&n,&m),tot = sy = sx = 0ll;
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
{
s = getchar();
while (s != '.' && s != '#') s = getchar();
tot += (s == '#'),sy += (s == '#') * i,sx += (s == '#') * j;
}
printf("%lld %lld\n",sy / tot,sx / tot);
}
return 0;
}
E. Secret Box
略。
#include<cstdio>
#include<cstring>
using namespace std;
int T,x,y,z;
long long k,ans;
long long max(long long x,long long y) { return x > y ? x : y ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d%d%lld",&x,&y,&z,&k),ans = 0ll;
for (int a = 1;a <= x;++ a)
if(!(k % a))
for (int b = 1;b <= y;++ b)
if(!(k % (a * b)))
{
long long c = k / (long long)(a * b);
if(!c || c > z) continue;
ans = max(ans,(long long)(x - a + 1ll) * (y - b + 1ll) * (z - c + 1ll));
}
printf("%lld\n",ans);
}
return 0;
}
F. Final Boss
二分。
#include<cstdio>
#include<cstring>
using namespace std;
#define N 200005
int T,h,n,a[N],c[N];
long long ans;
int check(long long x)
{
long long tmp = 0ll;
for (int i = 1;i <= n;++ i)
{
tmp += 1ll * ((x - 1ll) / c[i] + 1ll) * a[i];
if(tmp >= h) return 1;
}
return 0;
}
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&h,&n),ans = 0ll;
for (int i = 1;i <= n;++ i) scanf("%d",&a[i]);
for (int i = 1;i <= n;++ i) scanf("%d",&c[i]);
long long l,r,mid;
l = 1,r = 40000000009;
while (l <= r)
{
mid = (l + r) >> 1;
if(check(mid)) ans = mid,r = mid - 1;
else l = mid + 1;
}
printf("%lld\n",ans);
}
return 0;
}
G. D-Function
简单思维题。
假设原数字的某一位为 m ,则必须满足 k*m < 10 ,即不能进位的意思,那么我们只需要求出 m 可能的最大值就可以快速幂计数了。
#include<cstdio>
#include<cstring>
using namespace std;
#define mod 1000000007
int T,l,r,k,p;
long long ans;
long long pow(long long x,int k)
{
long long tmp = 1ll;
while (k)
{
if(k & 1) tmp = tmp * x % mod;
k >>= 1;
x = x * x % mod;
}
return tmp;
}
long long tot(int x)
{
if(!x) return 1ll;
return 1ll * pow(p + 1ll,x) % mod;
}
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d%d",&l,&r,&k);
if(k > 9)
{
printf("0\n");
continue;
}
p = 0;
while (k * (p + 1) < 10) ++ p;
ans = ((tot(r) - tot(l)) % mod + mod) % mod;
printf("%lld\n",ans);
}
return 0;
}
H1. Maximize the Largest Component (Easy Version)
用并查集(dfs也可以)统计每个连通块的大小,暴力枚举每一行,那么每一行的贡献就是这一行上的 "." 的数目加上与其相邻的行上连通块的大小之和。列的道理是一样,求最大值即可。
#include<cstdio>
#include<cstring>
using namespace std;
#define N 1000006
int T,n,m,a[N],fa[N],siz[N],vis[N],bz[N],cnt,ans;
int get(int x,int y) { return (x - 1) * m + y ; }
int max(int x,int y) { return x > y ? x : y ; }
int find(int x)
{
if(fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
memset(a,0,sizeof a);
memset(fa,0,sizeof fa);
memset(siz,0,sizeof siz);
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&n,&m),ans = 0;
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
{
int tmp = get(i,j);
char s;
scanf(" %c",&s);
a[tmp] = (s == '.') ? 0 : 1,fa[tmp] = tmp,siz[tmp] = 1,vis[tmp] = 0;
if(!a[tmp]) continue;
ans = 1;
int nor = get(i - 1,j);
int wes = get(i,j - 1);
if(i > 1 && a[nor]) siz[find(nor)] += siz[find(tmp)],fa[find(tmp)] = find(nor),ans = max(ans,siz[find(nor)]);
if(j > 1 && a[wes] && find(tmp) != find(wes)) siz[find(wes)] += siz[find(tmp)],fa[find(tmp)] = find(wes),ans = max(ans,siz[find(wes)]);
}
for (int i = 1;i <= n;++ i)
{
cnt = 0;
int tmp = 0;
for (int j = 1;j <= m;++ j)
{
int now = get(i,j);
int nor = get(i - 1,j);
int sou = get(i + 1,j);
tmp += (!a[now]);
if(a[now] && !vis[find(now)]) vis[find(now)] = 1,tmp += siz[find(now)],bz[++ cnt] = find(now);
if(i > 1 && a[nor] && !vis[find(nor)]) vis[find(nor)] = 1,tmp += siz[find(nor)],bz[++ cnt] = find(nor);
if(i < n && a[sou] && !vis[find(sou)]) vis[find(sou)] = 1,tmp += siz[find(sou)],bz[++ cnt] = find(sou);
}
ans = max(ans,tmp);
for (int j = 1;j <= cnt;++ j) vis[bz[j]] = 0;
}
for (int j = 1;j <= m;++ j)
{
cnt = 0;
int tmp = 0;
for (int i = 1;i <= n;++ i)
{
int now = get(i,j);
int wes = get(i,j - 1);
int eas = get(i,j + 1);
tmp += (!a[now]);
if(a[now] && !vis[find(now)]) vis[find(now)] = 1,tmp += siz[find(now)],bz[++ cnt] = find(now);
if(j > 1 && a[wes] && !vis[find(wes)]) vis[find(wes)] = 1,tmp += siz[find(wes)],bz[++ cnt] = find(wes);
if(j < m && a[eas] && !vis[find(eas)]) vis[find(eas)] = 1,tmp += siz[find(eas)],bz[++ cnt] = find(eas);
}
ans = max(ans,tmp);
for (int i = 1;i <= cnt;++ i) vis[bz[i]] = 0;
}
printf("%d\n",ans);
}
return 0;
}
H2. Maximize the Largest Component (Hard Version)
如果仍然按照 Easy Version 的做法,那么时间复杂度太大且不方便处理重合的部分,考虑另一种计数方法。
既然直接求每一对行&列的贡献不行,那就思考每一个连通块能贡献到的行 / 列。
假设每个连通块的上下左右界分别是:nor,sou,wes,eas,则它能贡献的行是:[nor - 1,sou + 1],它能贡献的列是:[wes - 1,eas + 1] 。于是对于每一个连通块,我们可以把它贡献的行列区间都先加上1,再考虑去重。
对每个连通块,重复计算的贡献为 行 [nor - 1,sou + 1] 和列 [wes - 1,eas + 1] 配对的情况,即一个左上角为 (nor - 1,wes - 1),右下角为 (sou + 1,eas + 1) 的矩形,那么我们只需要再维护一个去重数组,对每个连通块的这部分重复矩形的贡献也加上1,最后计算答案的时候减去即可。
为了在时间上更优秀,可以使用差分的方法。
当然别忘了还有最后一部分贡献是枚举的行和列上 "." 的数目。
#include<cstdio>
#include<cstring>
using namespace std;
#define N 2000006
int T,n,m,a[N],fa[N],siz[N],nor[N],sou[N],wes[N],eas[N],vis[N],id[N],dr[N],dl[N],ds[N],l[N],r[N],s[N],fl[N],fr[N],cnt,ans;
int get(int x,int y) { return (x - 1) * m + y ; }
int min(int x,int y) { return x < y ? x : y ; }
int max(int x,int y) { return x > y ? x : y ; }
int find(int x)
{
if(fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
memset(a,0,sizeof a);
memset(fa,0,sizeof fa);
memset(siz,0,sizeof siz);
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&n,&m),ans = 0;
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
{
int tmp = get(i,j);
char st;
scanf(" %c",&st);
a[tmp] = (st == '.') ? 0 : 1,fa[tmp] = tmp,siz[tmp] = 1,vis[tmp] = 0,nor[tmp] = sou[tmp] = i,wes[tmp] = eas[tmp] = j;
if(!a[tmp]) continue;
ans = 1;
int tn = get(i - 1,j);
int tw = get(i,j - 1);
if(i > 1 && a[tn])
{
int rn = find(tn);
siz[rn] += siz[tmp],fa[tmp] = rn,ans = max(ans,siz[rn]);
sou[rn] = max(sou[rn],i);
}
if(j > 1 && a[tw] && find(tmp) != find(tw))
{
int rt = find(tmp);
int rw = find(tw);
siz[rw] += siz[rt],fa[rt] = rw,ans = max(ans,siz[rw]);
nor[rw] = min(nor[rw],nor[rt]),eas[rw] = max(eas[rw],eas[rt]),wes[rw] = min(wes[rw],wes[rt]);
}
}
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
id[get(i,j)] = find(get(i,j));
for (int i = 0;i <= n + 1;++ i)
{
r[i] = dr[i] = 0;
for (int j = 0;j <= m + 1;++ j)
l[j] = s[get(i,j)] = dl[j] = ds[get(i,j)] = 0;
}
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
{
int now = id[get(i,j)];
if(!vis[now] && a[now])
{
vis[now] = 1;
dr[max(nor[now] - 1,1)] += siz[now],dr[min(sou[now] + 2,n + 1)] -= siz[now];
dl[max(wes[now] - 1,1)] += siz[now],dl[min(eas[now] + 2,m + 1)] -= siz[now];
ds[get(max(nor[now] - 1,1),max(wes[now] - 1,1))] += siz[now],ds[get(min(sou[now] + 2,n + 1),min(eas[now] + 2,m + 1))] += siz[now];
ds[get(max(nor[now] - 1,1),min(eas[now] + 2,m + 1))] -= siz[now],ds[get(min(sou[now] + 2,n + 1),max(wes[now] - 1,1))] -= siz[now];
}
}
r[0] = l[0] = s[0] = 0;
for (int i = 1;i <= n;++ i)
{
r[i] = r[i - 1] + dr[i],fr[i] = 0;
for (int j = 1;j <= m;++ j) fr[i] += (!a[get(i,j)]);
}
for (int j = 1;j <= m;++ j)
{
l[j] = l[j - 1] + dl[j],fl[j] = 0;
for (int i = 1;i <= n;++ i) fl[j] += (!a[get(i,j)]);
}
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
s[get(i,j)] = s[get(i - 1,j)] + s[get(i,j - 1)] - s[get(i - 1,j - 1)] + ds[get(i,j)];
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
ans = max(ans,r[i] + l[j] - s[get(i,j)] + fr[i] + fl[j] - (!a[get(i,j)]));
printf("%d\n",ans);
}
return 0;
}