[ABC407]

[ABC407C]

我们发现,后面的操作会影响到前面的操作。

当处理到第 \(i\) 个数时,我们知道,若干次操作后后面的一位会从 \(0\) 变为 \(a_{i+1}\),对于这个数的影响即为增加 \(a_{i+1}\)。为了让它成为 \(a_i\),我们必须减去 \(a_{i+1}\) 这一影响,然后计算需要的累加次数。

显然,对于每一位都如此,将所有结果累加即可得到答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
ll ans,len,a[500010];
int main(){
	cin>>s;
	ll len=s.length();
	for(int i=0;i<len;i++)
		a[i]=s[i]-48;
	for(int i=len-1;i>=0;i--){
		ans+=(a[i]-a[i+1]+10)%10;
	}
	cout<<ans+len<<endl;
	return 0;
}

[ABC407D]

注意到范围很小,可以搜索。

为了避免重复搜索,视一个方块的左上角为块锚点,每次仅在上一次放置的的块锚点下面的行或者在上一次放置的块锚点所在的行右侧的格子放置块锚点,并对可能的两种情况搜索。

因为数据范围小所以可以直接通过。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[26][26],h,w,s,ans;
bool o[26][26];
void dfs(ll x,ll y){
	ans=max(ans,s);
	for(int i=1;i<=h;i++){
		for(int j=1;j<=w;j++){
			if(o[i][j]||x*20+y>=i*20+j) continue;
			o[i][j]=true;
			s^=a[i][j];
			if(j<w&&!o[i][j+1]){
				s^=a[i][j+1];
				o[i][j+1]=true;
				dfs(i,j);
				s^=a[i][j+1];
				o[i][j+1]=false;
			}
			if(i<h&&!o[i+1][j]){
				s^=a[i+1][j];
				o[i+1][j]=true;
				dfs(i,j);
				s^=a[i+1][j];
				o[i+1][j]=false;
			}
			o[i][j]=false;
			s^=a[i][j];
		}
	}
}
int main(){
	cin>>h>>w;
	for(int i=1;i<=h;i++){
		for(int j=1;j<=w;j++){
			cin>>a[i][j];
			s^=a[i][j];
		}
	}	
	ans=s;
	dfs(0,0);
	cout<<ans<<endl;
	return 0;
}

[ABC407E]

从左往右,第 \(i\) 个右括号仅可以在第 \(2i\) 个数字及其右侧的数字取,因为其左侧有至少 \(i\) 个左括号和 \(i-1\) 个右括号。

由此,我们将问题变为一个贪心选取数字的问题。我们可以从最右侧的右括号开始,每次将新的可移出元素加进来(\(a_{2i},a_{2i+1}\))然后移出最小的值。最后移出 \(N\) 个值后即可得到答案。

官方题解是从前往后做的,其实做法大同小异。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,a[400010],ans;
priority_queue<ll> q;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		ans=0;
		for(int i=1;i<=2*n;i++){
			cin>>a[i];
			ans+=a[i];
		}
		for(int i=n;i>=1;i--){
			q.push(-a[2*i]);
			ans+=q.top();
			q.pop();
			q.push(-a[2*i-1]);
		}
		cout<<ans<<endl;
		while(q.size()>0) q.pop();
	}
	return 0;
}

[ABC407F]

场后 12 分钟调出来,求前驱后继的单调栈用单向队列写可谓幽默至极。

计一个数的前驱为在其左侧,比它大,且与它距离最近的数;后继为在其右侧,比它大,且与它距离最近的数。特别的,若左侧没有比它大的数,则前驱为 \(0\);若右侧没有比它大的数,则前驱为 \(N+1\)

考虑一个数的贡献随 \(k\) 的变化,当区间长为 \(k\) 时:

  • 当不存在一个包含 \(a_i\) 的区间包含其前驱或后继,那么它就会比区间长为 \(k-1\) 时多一次贡献。
  • 当存在包含 \(a_i\) 的区间包含其前驱或后继,但是不同时存在包含 \(a_i\) 的区间包含其前驱和包含 \(a_i\) 的区间包含其后继,那么它就会和区间长为 \(k-1\) 时贡献相同。
  • 当存在一个包含 \(a_i\) 的区间包含其前驱,也存在一个包含 \(a_i\) 的区间包含其后继,那么它就会比区间长为 \(k-1\) 时少一次贡献。

可以注意到贡献随 \(k\) 增长是两段等差数列和一段恒定值的结合,对于每个数,设置其贡献的差分都是两个区间加操作,用差分维护差分就可以了。至于前驱后继用单调栈做就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n,a[200010],l[200010],r[200010],s[200010];
deque<ll> q;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		r[i]=n+1;
	}
	for(int i=1;i<=n;i++){
		while(q.size()>0&&a[q.back()]<a[i]) q.pop_back();
		if(q.size()>0) l[i]=q.back();
		q.push_back(i);
	}
	while(q.size()>0) q.pop_back();
	for(int i=n;i>=1;i--){
		while(q.size()>0&&a[q.back()]<=a[i]) q.pop_back();
		if(q.size()>0) r[i]=q.back();
		q.push_back(i);
	}
	for(int i=1;i<=n;i++){
		s[0]+=a[i];
		s[i-l[i]]-=a[i];
		s[r[i]-i]-=a[i];
		s[r[i]-l[i]]+=a[i];
	}
	for(int i=1;i<=n;i++) s[i]+=s[i-1];
	for(int i=1;i<=n;i++) s[i]+=s[i-1];
	for(int i=0;i<n;i++) cout<<s[i]<<endl;
	return 0;
}
posted @ 2025-05-24 23:47  羽绘  阅读(78)  评论(0)    收藏  举报