[R18][中国語翻訳]HDKのABC370赛試(ABC370)

A.Raise Both Hands \(\texttt{Diff }11\)

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define void inline void
// #define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
#define test(i) cout<<"test: "<<i<<endl
#define testp(i,j) cout<<i<<" "<<j<<endl
#define testd(i) cout<<i<<" "
#define end cout<<"\n"
#define div <<" "<<
#else
#define test(i)
#define testp(i,j)
#define testd(i)
#define end false
#define div ,
#endif
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
int main(){
	int l,r;
	cin>>l>>r;
	if(l==1 and r!=1){
		cout<<"Yes";
	}
	else if(l!=1 and r==1){
		cout<<"No";
	}
	else{
		cout<<"Invalid";
	}
}

B.Binary Alchemy \(\texttt{Diff }84\)

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define void inline void
// #define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
#define test(i) cout<<"test: "<<i<<endl
#define testp(i,j) cout<<i<<" "<<j<<endl
#define testd(i) cout<<i<<" "
#define end cout<<"\n"
#define div <<" "<<
#else
#define test(i)
#define testp(i,j)
#define testd(i)
#define end false
#define div ,
#endif
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
int n;
int a[101][101];
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=i;++j){
			cin>>a[i][j];
		}
	}
	int st=1;
	for(int i=1;i<=n;++i){
		if(st>=i){
			st=a[st][i];
		}
		else{
			st=a[i][st];
		}
	}
	cout<<st<<endl;
	
}

C.Word Ladder \(\texttt{Diff }228\)

给你两个字符串 \(S,T\),长度相等,要求你每次改变 \(S\) 一个字符,最后令 \(S=T\),要求在过程中的每一步都使 \(S\) 字典序尽可能最小,输出过程

考虑到当 \(S_{i}\lt T_{i}\) 的时候应该最先改,否则应该最后改. 因此正着遍历一遍跑出 \(S_{i}\lt T_{i}\) 的,剩下的倒着跑出来即可.

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define void inline void
// #define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
#define test(i) cout<<"test: "<<i<<endl
#define testp(i,j) cout<<i<<" "<<j<<endl
#define testd(i) cout<<i<<" "
#define end cout<<"\n"
#define div <<" "<<
#else
#define test(i)
#define testp(i,j)
#define testd(i)
#define end false
#define div ,
#endif
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
string s,t;
vector<string>ss;
int main(){
	cin>>s>>t;
	while(s!=t){
		for(int i=0;i<=s.length()-1;++i){
			if(s[i]>t[i]){
				s[i]=t[i];
				ss.push_back(s);
			}
		}
		for(int i=s.length()-1;i>=0;--i){
			if(s[i]<t[i]){
				s[i]=t[i];
				ss.push_back(s);
			}
		}
	}
	cout<<ss.size()<<endl;
	for(string i:ss) cout<<i<<endl;
}

D.Cross Explosion \(\texttt{Diff }1088\)

给定 \(H\times W\) 的矩阵,初始全是墙,\(Q\) 次询问,每次给出一个坐标 \((x,y)\) 进行如下操作:

  • \((x,y)\) 是墙,直接摧毁
  • 否则,摧毁 \((x,y)\) 在上下左右四个方向上距离最近的墙,该方向上没有墙则不摧毁

求最终的墙数

\(H\times W\le 4\times 10^{5},Q\le 2\times 10^{5}\)

考察对 STL 的用法

考虑对横行和数列分别建 set,记录当前行/列存在墙的坐标,每次删除的时候,只需要进入对应行/列的 set 直接查找删除即可

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define void inline void
 #define ONLINE_JUDGE
//#ifndef ONLINE_JUDGE
//#define test(i) cout<<"test: "<<i<<endl
//#define testp(i,j) cout<<i<<" "<<j<<endl
//#define testd(i) cout<<i<<" "
//#define div <<" "<<
//#else
//#define test(i)
//#define testp(i,j)
//#define testd(i)
//#define end false
//#define div ,
//#endif
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
int n,m,q;
set<int>mp[400001],mp2[400001];
int main(){
	read(n,m,q);
	int ans=n*m;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			mp[i].insert(j);
			mp2[j].insert(i);
		}
	}
	while(q--){
		int x,y;
		read(x,y);
		auto it=mp[x].lower_bound(y);
		if(it!=mp[x].end() and *it==y){
			mp[x].erase(it);
			mp2[y].erase(mp2[y].lower_bound(x));
			ans--;
		}
		else{
			if(it!=mp[x].begin()){
				it--;
				mp2[*it].erase(mp2[*it].lower_bound(x));
				mp[x].erase(it);
				ans--;
			}
			auto it2=mp[x].upper_bound(y);
			if(it2!=mp[x].end()){
				mp2[*it2].erase(mp2[*it2].lower_bound(x));
				mp[x].erase(it2);
				ans--;
			}
			auto it3=mp2[y].lower_bound(x);
			if(it3!=mp2[y].begin()){
				it3--;
				mp[*it3].erase(mp[*it3].lower_bound(y));
				mp2[y].erase(it3);
				ans--;
			}
			auto it4=mp2[y].upper_bound(x);
			if(it4!=mp2[y].end()){
				mp[*it4].erase(mp[*it4].lower_bound(y));
				mp2[y].erase(it4);
				ans--;
			}
		}
	}
	cout<<ans<<endl;
}

需要注意 set 一定要用内置的 lower_bound() 函数!! ,否则复杂度直接多一个 \(n\log n\)

本题还有并查集做法,参见 \(\color{Red}\text{l}\color{black}\text{xyt}\ 大佬\) 的提交

E.Avoid K Partition \(\texttt{Diff }1453\)

给定一个数列,划分为若干非空子序列,要求没有任何子序列的和等于 \(K\),求方案数

\(N\le 2\times 10^{5}\)

考虑设计 \(f_{i}\) 表示考虑前 \(i\) 位的方案数,那么我们在从大到小枚举 \(i\) 的同时寻找断点,容易得到

\[f_{i}=\sum\{f_{j}[sum_{i}-sum_{j}\neq K]\} \]

其中 \(sum\) 为前缀和数组.

因此有了 \(n^{2}\) 的解法,注意到 \(sum_{i}-sum_{j}=K\) 的数极少,因此可以提前处理出 \(\sum_{j}^{i-1}f_{j}\),然后直接减去不合法的即可,寻找不合法元素可以用 set 实现,复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define void inline void
template<typename T>
void read(T& x){
	x=0;bool sym=0;char c=getchar();
    while(!isdigit(c)){sym^=(c=='-');c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
    read(x);read(args...);
}
#define tests int cases;read(cases);while(cases--)
#define pb bush_back
const int p=998244353;
long long n,k,a[200001];
int main(){
	read(n,k);
	for(int i=1;i<=n;++i){
		read(a[i]);
	}
	map<long long,long long>mp;
	mp[0]=1;
	long long ans=1;
	long long sum=0;
	for(int i=1;i<=n;++i){
		sum+=a[i];
    	int res;
		if(mp.count(sum-k)) res=ans-mp[sum-k];
		else res=ans;
    	mp[sum]=(mp[sum]+res+p)%p;
		ans=(ans+res+p)%p;
    	if(i==n) cout<<((res+p)%p)<<endl;
	}
}


F.Cake Division

\(N\) 个元素的环形数列,分成 \(K\) 个连续段,最大化每个连续段和的最小值

并且求出在所有可能的方案中,哪两个元素在所有方案中都在同一个连续段内

\(N\le 2\times 10^{5}\)

posted @ 2024-09-08 17:19  HaneDaniko  阅读(69)  评论(5编辑  收藏  举报