AtCoder Beginner Contest 216 题解

比赛地址: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\) 随便选的情况的答案,这时要满足的条件变成了:

\[a_k\ge b_k+\sum_{i\subseteq\{k+1,k+2,\cdots,n\}}b_i \]

后面的 \(\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);
}
posted @ 2021-08-29 22:23  registerGen  阅读(122)  评论(0编辑  收藏  举报