牛客2025多校 R4

牛客2025多校 R4

F:

题目大意:

image-20250727183719783

void solve(){
	LL n,k,c;
	cin>>n>>k>>c;
	vector<LL> a(n);
	for (int i=0;i<n;i++) cin>>a[i];
	for (int i=0;i<n;i++) a[i]=a[i]-c*i;
	sort(a.begin(),a.end(),[](int x,int y){return x>y;});
	LL sum=0;
	for (int i=0;i<k;i++) sum+=a[i];
	cout<<sum+c*k*(k-1)/2<<endl;
}

显然有个贪心的思维是如果有 \(k\) 个被选出来的数,他们在交换操作的前后相对位置不改变

所以考虑先将所有数都交换至第一个位置上的权值,从大到小排序后取前 \(k\) 个数,最后减去这 \(k\) 个数依次排开的贡献

\[a_i\leftarrow a_i-c*i\\ \mathrm{sort}(a)\\ ans=\sum_{i=1}^k a_i +\frac{c*(k-1)*k}{2} \]

B:

题目大意:

image-20250727184110700

int n,m,k;
bool ans;
vector<vector<char>> g;
vector<vector<int>> val;
vector<vector<bool>> vis;
vector<vector<bool>> st;

void dfs1(int x,int y){
	if (x<1||y<1||x>n||y>m) return;
	if (g[x][y]=='1') return;
	if (vis[x][y]) return;
	vis[x][y]=1;
	dfs1(x+1,y);
	dfs1(x-1,y);
	dfs1(x,y-1);
}

void dfs2(int x,int y){
	if (x<1||y<1||x>n||y>m) return;
	if (g[x][y]=='1') return;
	if (st[x][y]) return;
	st[x][y]=1;
	if (vis[x][y]==0) ans=1;
	if (val[x][y+1]>=k+y) dfs2(x,y+1);
	dfs2(x+1,y);
	dfs2(x-1,y);
}

void solve(){
	cin>>n>>m>>k;
	g.assign(n+1,vector<char>(m+1));
	val.assign(n+1,vector<int>(m+1,0));
	vis.assign(n+1,vector<bool>(m+1,0));
	st.assign(n+1,vector<bool>(m+1,0));
	ans=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			cin>>g[i][j];
	for (int j=m;j>=1;j--){
		for (int i=1;i<=n;i++)
			if (g[i][j]=='0') val[i][j]=max(val[i][j],j);
		for (int i=1;i<n;i++)
			if (g[i][j]=='0'&&g[i+1][j]=='0') val[i+1][j]=max(val[i][j],val[i+1][j]);
		for (int i=n-1;i>=1;i--)
			if (g[i][j]=='0'&&g[i+1][j]=='0') val[i][j]=max(val[i][j],val[i+1][j]);
		if (j>1) for (int i=1;i<=n;i++)
			if (g[i][j]=='0'&&g[i][j-1]=='0') val[i][j-1]=val[i][j];
	}
	
	dfs1(1,m);
	dfs2(1,1);
	if (ans) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
}

val[i][j] 表示在 \(i,j\) 的位置上可以看到的最右边的列坐标,因为可以自由上下走,所以还需要对当前的第 \(j\) 列都维护出来

反向 DFS 一次,把所有可以到终点的路径都标记下来。最后再正向 DFS,模拟马里奥行走的模式

当在 \(i,j\) 的位置上能向右走时,当且仅当下一步的 val[x][y+1] 的视野内仍无法判断是否在死胡同上

如果在死胡同上(vis[x][y]==0),那么就存在这样的一个满足要求的路径

G:
题目大意:

image-20250727214619931

const int mod=998244353;

LL ksm(LL a,LL b,LL p){
	LL res=1;
	while (b){
		if (b&1) res=res*a%p;
		a=a*a%p;
		b>>=1;
	}
	return res;
}

void solve(){
	string s;
	cin>>s;
	int n=s.size();
	vector<int> a(n+1);
	for (int i=0;i<n;i++)
		a[i+1]=(s[i]=='('?1:-1);
	vector<int> pre(n+1);
	for (int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i];
	vector<int> lt(n+2);
	for (int i=1;i<=n;i++) lt[i]=lt[i-1]+(s[i-1]=='(');
	vector<int> rt(n+2);
	for (int i=n;i>=1;i--) rt[i]=rt[i+1]+(s[i-1]==')');
	int p=1;
	LL ans=0;
	for (int i=1;i<=n;i++){
		p=max(p,i);
		while (p<=n&&pre[p]>1) p++;
		if (s[i-1]=='(')
			ans=(ans+ksm(2,lt[i-1],mod)*ksm(2,rt[p+1],mod)%mod)%mod;
	}
	ans=(ans+ksm(2,n/2,mod))%mod;
	cout<<ans*ksm(ksm(2,n,mod),mod-2,mod)%mod<<endl;
}

如果当前的 '?' 只能被解读为 '(' ,那么说明该重建唯一,考虑一个合法的前缀串的性质是 '(' 的数量大于等于 ')' 的数量

将 '(' 视为 \(1\) ,')' 视为 \(-1\) ,那么一个合法的前缀串当且仅当前缀和大于等于 \(0\)

对于每个 '(' ,我都想知道将它变成 ')' 后是否合法,如果不合法,那么重建唯一

所以找一段连续的子串,当这一段子串的部分和为 \(0\) 时,说明其中的 '(' 不能被替换,这就是一种可能的方案

这个子串左边的部分和右边的部分相对于子串独立,即子串左边的 '(' 和子串右边的 ')' 不影响子串内方案的唯一性

for (int i=1;i<=n;i++){
	p=max(p,i);
	while (p<=n&&pre[p]>1) p++;//找满足条件的右边界
	if (s[i-1]=='(')
		ans=(ans+ksm(2,lt[i-1],mod)*ksm(2,rt[p+1],mod)%mod)%mod;
}

类似于双指针的性质,p 指针和遍历字符串的 i 指针都只会遍历字符串一次,时间复杂度是线性的

I:
题目大意:

image-20250727214730072

int n,m;
vector<vector<char>> g;
int scc[60][60],idx;
int box[3000],des[3000];
int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};

pair<int,int> pre[60][60];
vector<pair<int,int>> path;
vector<pair<pair<int,int>,pair<int,int>>> ans;

void bfs(int sx,int sy,int num){
	queue<pair<int,int>> q;
	q.push({sx,sy});
	scc[sx][sy]=num;
	while (q.size()){
		auto t=q.front();
		q.pop();
		
		if (g[t.first][t.second]=='@') box[num]++;
		if (g[t.first][t.second]=='*') des[num]++;
		if (g[t.first][t.second]=='!') box[num]++,des[num]++;
		
		for (int i=0;i<4;i++){
			int tx=t.first+dx[i],ty=t.second+dy[i];
			if (tx<1||ty<1||tx>n||ty>m) continue;
			if (g[tx][ty]=='#') continue;
			if (scc[tx][ty]) continue;
			scc[tx][ty]=num;
			q.push({tx,ty});
		}
	}
}

void bfs2(int sx,int sy){
	queue<pair<int,int>> q;
	q.push({sx,sy});
	vector<vector<bool>> vis(n+1,vector<bool>(m+1,0));
	vis[sx][sy]=1;
	while (q.size()){
		auto t=q.front();
		q.pop();
		
		if (g[t.first][t.second]=='*'){
			int x=t.first,y=t.second;
			do{
				path.push_back({x,y});
				int tx=x,ty=y;
				x=pre[tx][ty].first,y=pre[tx][ty].second;
			}while (x!=sx||y!=sy);
			path.push_back({sx,sy});
			return;
		}
		
		for (int i=0;i<4;i++){
			int tx=t.first+dx[i],ty=t.second+dy[i];
			if (tx<1||ty<1||tx>n||ty>m) continue;
			if (g[tx][ty]=='#') continue;
			if (vis[tx][ty]) continue;
			vis[tx][ty]=1;
			pre[tx][ty]={t.first,t.second};
			q.push({tx,ty});
		}
	}
}

void makepath(vector<pair<int,int>> &path){
	int p=0;
	for (int i=0;i<path.size();i++){
		if (g[path[i].first][path[i].second]!='.'){
			for (int j=i;j>p;j--)
				ans.push_back({path[j],path[j-1]});
			p=i;
		}
	}
	g[path.back().first][path.back().second]='.';
	g[path.front().first][path.front().second]='!';
}

void solve(){
	cin>>n>>m;
	g.assign(n+1,vector<char>(m+1));	
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			cin>>g[i][j];
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (g[i][j]!='#'&&scc[i][j]==0)
				bfs(i,j,++idx);
	
	for (int i=1;i<=idx;i++){
		if (box[i]!=des[i]){
			cout<<-1;
			return;
		}
	}
	
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			if (g[i][j]=='@'){
				path.clear();
				bfs2(i,j);
				makepath(path);
			}
		}
	}
	
	auto getpos=[](pair<int,int> a,pair<int,int> b){
		int dx=b.first-a.first;
		int dy=b.second-a.second;
		if (dx>0) return 'D';
		else if (dx<0) return 'U';
		else if (dy>0) return 'R';
		else return 'L';
	};
	
	cout<<ans.size()<<endl;
	for (int i=0;i<ans.size();i++)
		cout<<ans[i].first.first<<' '<<ans[i].first.second<<' '<<getpos(ans[i].first,ans[i].second)<<'\n';
}

墙壁将图分割为有限个连通块,当且仅当每个连通块内箱子数和终点数相同时才会有解

所以先 BFS 一次搜索所有连通块,判断是否有解

然后是对路径的维护,考虑这样一个问题:一个箱子移动到终点的路径上如果有其他箱子阻挡该怎么办

事实上可以看作交换这两个箱子,即形式化的在箱子 \(a_1\) 移动到终点的过程中会被 \(a_2,a_3,\cdots,a_i\) 这些箱子阻挡

那么可以先将 \(a_i\) 移动到终点,\(a_{i-1}\) 移动到 \(a_i\) ,这样操作下来可以视作将 \(a_1\) 移动到了终点

最后在对每个箱子都做一遍 BFS 找到距离最近的终点,然后利用前导数组记录下移动的路径

最后模拟一次将 \(a_i\) 移动到终点,\(a_{i-1}\) 移动到 \(a_i\) 的过程即可

int p=0;
for (int i=0;i<path.size();i++){
	if (g[path[i].first][path[i].second]!='.'){//如果遇到了拦路的箱子
		for (int j=i;j>p;j--)
			ans.push_back({path[j],path[j-1]});//先将这个箱子移动走
		p=i;
	}
}

D:
题目大意:

image-20250727214750542

int g[201][201];

void solve(){
	int N;
	cin>>N;
	int n=200;
	for (int i=1;i<=n;i++) g[i][i]=1;
	g[n][n]=N;
	for (int i=n-1,v=1;i>=1;i-=2,v^=1){
		if (N&1){
			if (v){
				g[i][n]=g[i-1][n]=-(N>>1);
				N>>=1;
			}else{
				g[i][n]=g[i-1][n]=(N+1)>>1;
				N=(N+1)>>1;
			}
		}else{
			if (v){
				g[i][n]=g[i-1][n]=-(N>>1);
				N>>=1;
			}else{
				g[i][n]=g[i-1][n]=(N>>1);
				N>>=1;
			}
		}
		if (N==0) break;
	}
	
	for (int i=n,v=1;i>=1;i--,v++){
		if (g[i][n]==0) break;
		if (v&1) g[i][i-1]=g[i][i-2]=1;
		else g[i][i-2]=g[i][i-3]=1;
		g[i][n]&=1;
	}
	
	cout<<n<<endl;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++)
			cout<<g[i][j]<<' ';
		cout<<endl;
	}
}

显然这样的一个矩阵的行列式为 \(N\)

\[\begin{vmatrix} 1&0&0&0&0\\ 0&1&0&0&0\\ 0&0&\cdots&0&0\\ 0&0&0&1&0\\ 0&0&0&0&N\\ \end{vmatrix} \]

因为需要构造的是一个 01 矩阵,所以考虑行变换将 \(N\) 用 01 表示

\(N\) 为正整数且是奇数时:\(N-2\lfloor \frac{N}{2}\rfloor=1\),如果为正偶数,有:\(N-2\lfloor \frac{N}{2}\rfloor=0\)

\(N\) 为负整数且是奇数时:\(N-2\lceil \frac{N}{2}\rceil=1\) ,如果为负偶数,有:\(N-2\lceil \frac{N}{2}\rceil=0\)

那么对于第 \(n\) 列,我们可以将 \(N\) 分解到每一行上,形如:

\[\begin{vmatrix} \cdots&0&0&0&\lceil \frac{-\lfloor \frac{N}{2}\rfloor}{2}\rceil\\ 0&1&0&0&\lceil \frac{-\lfloor \frac{N}{2}\rfloor}{4}\rceil\\ 1&1&1&0&-\lfloor \frac{N}{2}\rfloor\\ 1&1&0&1&-\lfloor \frac{N}{2}\rfloor\\ 0&0&1&1&N\\ \end{vmatrix} \]

即对于第 \(j\) 行,总是可以由 \(j-1,j-2\) 这两行通过行变化使得 \(A_{j,n}\)\(0,1\)

posted @ 2025-08-02 16:29  才瓯  阅读(13)  评论(0)    收藏  举报