ABC466 赛后总结

共 1302 字
11 分钟
0 次阅读

本场 Rating:$\textcolor{#00C0C0}{1484} \rightarrow \textcolor{#00C0C0}{1591}(+107)$。

本场表现分:$\textcolor{#C0C000}{2206}$。

下场好像要上蓝了。

成功连续做出来三场 F。简称:三连击。

注意 ABC464F > ABC465F > ABC466F。

A Compromise

你说的对,但是 $0$ 不是负数(?)。

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include<bits/stdc++.h>
using namespace std;
int main(){
 	int n;
	scanf("%d",&n);
	while(n--){
		int t;
		scanf("%d",&t);
		if(t>=0){
			puts("No");
			return 0;
		}
	} 
	puts("Yes");
	return 0;
}

:::

B Representative Balls

出出原。Auto timu Copying.

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include<bits/stdc++.h>
using namespace std;
int clr[105];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	memset(clr,0xff,sizeof(clr));
	for(int i=1;i<=n;i++){
		int c,s;
		scanf("%d%d",&c,&s);
		clr[c]=max(clr[c],s);
	}
	for(int i=1;i<=m;i++){
		printf("%d ",clr[i]);
	}
	return 0;
}

:::

C Count Close Pairs

交互出现在 abc 的 C 里,我怎么会做这样的梦。

如果你做过 Practice Contest 的 B Interactive Sorting,那你一定记得其中有一句话:

interactive

哎呦我去这个红字放在这里好吓人。

Please note that these are not beginner questions.

祖宗之法可以变!


由于点的位置单调递增,所以对于每个点,最后一个距离在 $1$ 以内的点的位置一定单调不降。维护一个双指针就可以做到线性。

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	int rgt=2,ans=0;
	for(int i=1;i<=n-1;i++){
		rgt=max(rgt,i+1);
		while(rgt<=n){
			cout<<"? "<<i<<" "<<rgt<<endl;
			string s;
			cin>>s;
			if(s=="No")break;
			rgt++;
		}
		ans+=rgt-i-1;
	}
	cout<<"! "<<ans<<endl;
	//1.2 1.3 1.4 1.k
	//2.k
	return 0;
}

:::

D Placing Rooks

有史以来最简单的 D?

由于在添加之前要移除所有当前行和当前列的点,于是每行、每列最多只有一个点。暴力维护即可。也算是对神秘 C 的补偿吧。

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>
using namespace std;
int col[300005];
int row[300005];
int main(){
	memset(col,0xff,sizeof(col));
	memset(row,0xff,sizeof(row));
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int r,c;
		scanf("%d%d",&r,&c);
		//找到当前列对应的行并清除
		row[col[c]]=-1;
		col[row[r]]=-1;
		row[r]=c;
		col[c]=r; 
	}
	int ans=0;
	for(int i=1;i<=n;i++)if(col[i]!=-1)ans++;
	printf("%d",ans);
	return 0;
}

:::

E Range Flip

如此小的 $K$ 很难让人不想到 $(N2^K)$ 级的做法,但是 $2 \times 10^5$ 摆在那里。

又根据 AtCoder 的传统,总复杂度一般是 $10^6 \sim 10^7$ 量级的。匹配到 $O(NK)$。

因此,考虑 DP。

设出状态 $dp_{i,j,k}$ 表示考虑到第 $i$ 张卡,已经有 $j$ 次反转,当前是否在一个被反转的段中。

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
int a[200005],b[200005];
ll delta[200005];
ll dp[200005][11][2];//dp i,j,k:处理到第i张卡片,已反转j次,且不在/在某段里 
int main(){
	scanf("%d%d",&n,&k);
	ll sum=0;
	for(int i=1;i<=n;i++){
		scanf("%d%d",a+i,b+i);
		delta[i]=b[i]-a[i];
		sum+=a[i];
	}
	memset(dp,-0x3f,sizeof(dp));
	dp[0][0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);//不选
			//选 
			if(j!=0)dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0])+delta[i]; 
		} 
	}
	ll ans=sum;
	for(int i=0;i<=k;i++)ans=max(ans,max(dp[n][i][0],dp[n][i][1])+sum);
	printf("%lld",ans);
	return 0;
}

:::

F Many Mod Calculation

首先注意到 $a$ 只有单调递减的那一部分才有意义,否则若 $a_i \geq a_{i-1}$,那么前面模过来已经小于 $a_i$ 了,没有用。

设过滤后的 $a$ 数组为 $b$,其中 $|b|=k$。

我们写一个搜索函数 dfs(ll v,int i) 表示在值域 $[0,v]$ 中满足 $x \mod b_{i+1} \mod b_{i+2} \mod \ldots \mod b_{k} = 0$ 的个数。令 $dp_i$ 表示 dfs(b[i]-1,i)

由于单调递减,不妨使用二分向后找到第一个可能产生有效模的位置。

则 $v+1$ 个数会被分为 $\lceil \frac{v+1}{b_j} \rceil$ 组。

其中 $\lfloor \frac{v+1}{b_j} \rfloor$ 是完整的几个以 $b_j$ 作为循环节的区间,每个区间答案即 $dp_j$。

剩下一个零块递归搜索。

由于每次都会进行取模,因此 dfs 运行一次的复杂度应当只有 $O(\log k \log V)$,足够快了。

:::success[code]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll>b;
int n;
ll x;
int k;
ll dp[200005];
//[0,V]中满足x%b[i]的%%%%%b[k-1]=0的个数 
ll dfs(ll v,int i){
	if(v<0)return 0;
	if(i>=k)return 1;//到头了
	auto it=lower_bound(b.begin()+i,b.end(),v,greater<ll>());
	if(it==b.end())return 1;//%不了 
	int j=it-b.begin();
	ll ans=(v+1)/b[j]*dp[j]+dfs((v+1)%b[j]-1,j+1);
	return ans;
}
void solve(){
	scanf("%d%lld",&n,&x);
	b.clear();
	for(int i=1;i<=n;i++){
		ll ai;
		scanf("%lld",&ai);
		if(b.empty()||ai<b.back())b.push_back(ai);
	}
	k=b.size();
	dp[k-1]=1;//0
	for(int j=k-2;j>=0;j--)dp[j]=dfs(b[j]-1,j+1);
	printf("%lld\n",dfs(x,0)-1);
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		solve();
	}
	return 0;
}

:::

G Segment Sum Constraints

怎么会是数位 DP 呢?

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计