第六届威海市大学生程序设计竞赛 题解合集

打了一下午,但是好像没做出来啥有水平题?

here.

B

考虑每次能猜中,当且仅当拿出的卡片颜色,是多数颜色。

经过手玩发现,无论怎么取卡片,猜中的总次数都是 \(\max(a,b)\)

所以答案就为 \(\max(a,b)\)

#include<bits/stdc++.h>
using namespace std;
int a,b;
int main(){
	cin>>a>>b;
	printf("%.8lf",1.0*max(a,b));
	return 0;
}

D

感觉无论怎么 DP 都没前途,因为状态数必然是 \(O(nm)\) 的。

还是从组合意义去考虑比较合适。

考虑一种很新的刻画方式:枚举第一次选择的位置,对选择位置的差分数组计数。

如果确定了第一个位置为 \(pos\),那么差分数组 \(a\) 应该满足:

  • \(a_i\) 是奇数;

  • \(\sum_{i=1}^{n-1} a_i \le n-pos\)

\(b_i=\frac{a_i+1}{2}\),那么 \(b\) 应当满足:

  • \(\sum_{i=1}^{n-1} b_i \le \frac{n+m-pos-1}{2}\)

考虑 \(\le\) 难以刻画,令 \(b_n=\frac{n+m-pos-1}{2}-\sum_{i=1}^{n-1} b_i+1\),这样就把限制转化为了:

  • \(\sum_{i=1}^{n} b_i =\frac{n+m-pos+1}{2}\)

直接插板就行。

这样答案为:

\[\sum_{i=1,i\bmod 2=1}^{n} \binom{\frac{n+m-pos-1}{2}}{m-1} \]

直接算可以做到 \(O(n\log v)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,m,ans,fac[2000005];
int binpow(int a,int b){
	if(!b) return 1;
	int res=binpow(a,b/2);
	if(b&1) return res*res%mod*a%mod;
	else return res*res%mod;
}
int C(int n,int m){
	if(n<0 || m<0 || n<m) return 0;
	return  fac[n]*binpow(fac[m],mod-2)%mod*binpow(fac[n-m],mod-2)%mod;
}
signed main(){
	cin>>n>>m;
	fac[0]=1;
	for(int i=1;i<=n+m;i++){
		fac[i]=fac[i-1]*i%mod;
	}
	for(int i=1;i<=n;i+=2){
		ans=(ans+C((n-i+m-1)/2,m-1))%mod;
	}
	cout<<ans;
	return 0;
}

E

根据初中数学知识,不难算出答案为:

\[3(n^2-2n+1)+2n-2 \]

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,a,b;
signed main(){
	cin>>t;
	while(t--){
		cin>>a;
		cout<<3*(a*a-2*a+1)+2*a-2<<'\n';
	}
	return 0;
}

G

题意很难读懂,但是读懂了好像就是简单题。

考虑对于每一组 \([H/L,H/L;?]\)\(?\) 分配的概率是独立的。

\([H,H;H]\) 为例,设 \([H,H;?]\) 出现次数为 \(a\)\([H,H,H]\) 出现次数为 \(a\),则 \(P=\frac{b}{a}\)

证明考虑反证调整法。

#include<bits/stdc++.h>
using namespace std;
string s;
int n,a[1000005];
struct node{
	int h,l,g;
}h[2][2];
double ans;
int main(){
	cin>>s;
	n=s.size();
	for(int i=0;i<s.size();i++){
		if(s[i]=='H') a[i+1]=1;
		else a[i+1]=0;
	}
	for(int i=3;i<=n;i++){
		int x=a[i-2],y=a[i-1],z=a[i];
		if(!z) h[x][y].l++;
		else h[x][y].h++;
	}
	int x=a[n-1],y=a[n];
	h[x][y].g++;
	for(int i=0;i<=1;i++){
		for(int j=0;j<=1;j++){
			node tmp=h[i][j];
			double k=tmp.l+tmp.h+tmp.g;
			if(!k) continue;
			k=log(k);
			if(tmp.h>0){
                ans+=tmp.h*(log(tmp.h)-k);
            }
            if(tmp.l>0){
                ans+=tmp.l*(log(tmp.l)-k);
            }
            if(tmp.g>0){
                ans+=tmp.g*(log(tmp.g)-k);
            }
		}
	}
	printf("%.6lf",ans);
	return 0;
}

H

根据裴蜀定理,一定有解。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,a,b;
signed main(){
	cout<<"Yes";
	return 0;
}

J

相当于求 \(x_i \le x_j,y_i \le y_j\) 的二维 LIS。

考虑按 \(x\) 排序,从小到大扫,用 BIT 维护 \(y\) 的 LIS,记录转移点即可回溯方案。

复杂度 \(O(n \log n)\)

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,m,p;
struct node{
	int x,y,id;
}h[400005];
bool cmp1(node a,node b){
	if(a.x!=b.x) return a.x<b.x;
	else return a.y<b.y;
}
bool cmp3(node a,node b){
	return a.id<b.id;
}
int pre[400005],dis[400005];
void getans(int x){
	if(pre[x]) getans(pre[x]);
	if(pre[x]){
		int l=h[pre[x]].x,r=h[pre[x]].y;
		while(l<h[x].x) cout<<"D",l++;
		while(r<h[x].y) cout<<"R",r++;
	}
}
#define lowbit(i) (i&(-i))
struct line{
	int val,id;
	line(){
		val=id=0;
	}
	bool operator<(const line &a)const{
		return val<a.val;
	}
}c[200005];
void modify(int x,line k){
	for(int i=x;i<=m;i+=lowbit(i)){
		c[i]=max(c[i],k);
	}
}
line query(int x){
	line ans;
	for(int i=x;i;i-=lowbit(i)){
		ans=max(ans,c[i]);
	}
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>p;
	for(int i=1;i<=p;i++){
		cin>>h[i].x>>h[i].y;
		h[i].id=i; 
	}
	h[++p]=(node){1,1,p};
	h[++p]=(node){n,m,p};
	sort(h+1,h+1+p,cmp1);
	for(int i=1;i<=p;i++){
		dis[i]=query(h[i].y).val+1;
		pre[i]=query(h[i].y).id;
		line tmp;
		tmp.id=i;
		tmp.val=dis[i];
		modify(h[i].y,tmp);
	}
	cout<<dis[p]-2<<'\n';
	getans(p);
	return 0;
}

M

考虑网络流。

将一个点拆成三个,分别记为 \((x,0/1/2)\),这样连边:

  • \(s \to (x,0)\),边权为 \(+\inf\)

  • \((x,0) \to (x,1)\),边权为 \(a_i\)

  • \((x,1) \to (x,2)\),边权为 \(b_i\)

  • \((X,2) \to t\),边权为 \(+\inf\)

  • 对于 DAG 上 \(x \to y\),连边 \((x,2) \to (y,1)\),边权为 \(+\inf\)

最小割即为答案。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,m,s,t,x,y,w,a,b,dcnt,id[105][3];
int head[200005],nxt[200005],targetx[200005],targetw[200005],targetf[200005],tot=1;
void add(int x,int y,int w,int f){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	targetx[tot]=y;
	targetw[tot]=w;
	targetf[tot]=f;
}
int dis[200005],now[200005];
queue<int> q;
bool bfs(){
	for(int i=1;i<=dcnt;i++){
		dis[i]=inf;
		now[i]=head[i];
	}
	dis[s]=1;
	q.push(s);
	while(q.size()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=nxt[i]){
			int y=targetx[i],w=targetw[i],f=targetf[i];
			if(dis[y]!=inf || w-f==0) continue;
			dis[y]=dis[x]+1;
			q.push(y);
		}
	}
	return dis[t]!=inf;
}
int dfs(int x,int sum){
	if(x==t) return sum;
	int res=0;
	for(int i=head[x];i;i=nxt[i]){
		int y=targetx[i],w=targetw[i],f=targetf[i];
		if(dis[y]!=dis[x]+1 || w-f==0) continue;
		int tp=dfs(y,min(sum,w-f));
		if(!tp) dis[y]=inf;
		else{
			targetf[i]+=tp;
			targetf[i^1]-=tp;
			sum-=tp;
			res+=tp;
		}
		if(!sum) break;
	}
	return res;
}
int dinic(){
	int  ans=0;
	while(bfs()) ans+=dfs(s,inf);
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		id[i][0]=++dcnt;
		id[i][1]=++dcnt;
		id[i][2]=++dcnt;
	}
	s=++dcnt;
	t=++dcnt;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		add(id[x][2],id[y][1],inf,0);
		add(id[y][1],id[x][2],0,0);
	}
	for(int i=1;i<=n;i++){
		cin>>a;
		add(s,id[i][0],inf,0);
		add(id[i][0],s,0,0);
		add(id[i][0],id[i][1],a,0);
		add(id[i][1],id[i][0],0,0);
	}
	for(int i=1;i<=n;i++){
		cin>>b;
		add(id[i][1],id[i][2],b,0);
		add(id[i][2],id[i][1],0,0);
	}
	cin>>x;
	add(id[x][2],t,inf,0);
	add(t,id[x][2],0,0);
	cout<<dinic();
	return 0;
}
posted @ 2025-03-09 19:32  _Kenma  阅读(174)  评论(0)    收藏  举报