比赛地址:https://atcoder.jp/contests/abc216。
只有 ABCDEF 的题解,GH 不会。
A模拟。
void mian(){
int x,y;
scanf("%d.%d",&x,&y);
if(0<=y&&y<=2)printf("%d-",x);
if(3<=y&&y<=6)printf("%d",x);
if(7<=y&&y<=9)printf("%d+",x);
puts("");
}
B
模拟。
const int N=1000;
std::string s[N+10],t[N+10];
int n;
void mian(){
std::cin>>n;
for(int i=1;i<=n;i++)
std::cin>>s[i]>>t[i];
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(s[i]==s[j]&&t[i]==t[j]){
puts("Yes");
return;
}
puts("No");
}
C
考虑遍历 \(n\) 的每一个二进制位,如果是 \(1\),就 \(\times 2\) 再 \(+1\),如果是 \(0\),就 \(\times 2\)。
ll n;
void mian(){
scanf("%lld",&n);
for(int i=59;i>=0;i--)
if((n>>i)&1LL)printf("BA");
else printf("B");
puts("");
}
D
如果某一堆中 \(x\) 在 \(y\) 上面但是另一堆中 \(y\) 在 \(x\) 上面,那么就不行。
以此类推,可以得到一个结论:把这些 \(\langle x,y\rangle\) 的关系连有向边,如果存在环就不行,否则就行。
const int N=2e5;
struct Edge{int to,nxt;}e[N*2+10];int head[N+10],tote=1;
inline void addEdge(int u,int v){e[++tote].to=v;e[tote].nxt=head[u];head[u]=tote;}
int n,m,ind[N+10];
bool topoSort(){
std::queue<int> q;
for(int i=1;i<=n;i++)
if(!ind[i])q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(--ind[v]==0)q.push(v);
}
}
for(int i=1;i<=n;i++)
if(ind[i])return 0;
return 1;
}
void mian(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int k,x,lst=-1;
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d",&x);
if(lst==-1)lst=x;
else addEdge(lst,x),ind[x]++;
}
}
puts(topoSort()?"Yes":"No");
}
E
贪心。把 \(n\) 个公园的 \(a\) 值扔进堆里,每次从堆中取出最大的元素 \(x\) 并统计到答案里,然后把 \(x-1\) 扔进堆里。
但是这样显然会 T,其实只需在草稿纸上人脑模拟一下这个过程就知道如何优化了。
int n;
ll k,a[N+10],ans;
void mian(){
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",a+i);
std::sort(a+1,a+n+1);
std::reverse(a+1,a+n+1);
ll cnt=0;
for(int i=1;i<=n;i++){
ll x=a[i]-a[i+1];
if(cnt+1LL*x*i<=1LL*k)ans+=1LL*i*(a[i]+a[i+1]+1)*x/2,cnt+=1LL*x*i;
else{
ll kk=k-cnt;
ll tmp1=kk/i,tmp2=kk%i;
ans+=1LL*i*(a[i]+a[i]-tmp1+1)*tmp1/2;
ans+=1LL*tmp2*(a[i]-tmp1);
break;
}
}
printf("%lld\n",ans);
}
F
首先把 \(a\) 从大到小排序,这样那个 \(\max\) 就没了。
枚举每个 \(1\le k\le n\),我们统计 \(a_k\) 必须选,\(a_{k+1}\sim a_n\) 随便选的情况的答案,这时要满足的条件变成了:
后面的 \(\sum\) 是一个集合选数,所以用背包 dp 做即可。然后你兴冲冲地写了代码,发现时间复杂度不对。
事实上那个 dp 可以用前缀和优化,这样时间复杂度就对了。
const int N=5000;
const int P=998244353;
struct Node{int a,b;};
int n;
Node a[N+10];
int f[N+10][N+10],sum[N+10],ans; // f[i][j] 表示 b[i] 到 b[n] 中,b[i] 必须选,和为 j 的方案
void mian(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].a);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].b);
std::sort(a+1,a+n+1,[](Node x,Node y){return x.a>y.a;});
for(int k=n;k>=1;k--){
f[k][a[k].b]=1;
// 以下三行注释是暴力 dp
// for(int i=k+1;i<=n;i++)
// for(int j=N;j>=a[i].b;j--)
// f[k][j]+=f[k][j-a[i].b],f[k][j]%=P;
for(int j=N;j>=a[k].b;j--)
f[k][j]+=sum[j-a[k].b],sum[j]+=f[k][j],f[k][j]%=P,sum[j]%=P;
}
for(int k=1;k<=n;k++){
for(int i=a[k].a;i>=1;i--)
ans+=f[k][i],ans%=P;
}
printf("%d\n",ans);
}