第六届威海市大学生程序设计竞赛 题解合集
打了一下午,但是好像没做出来啥有水平题?
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}\)
直接插板就行。
这样答案为:
直接算可以做到 \(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
根据初中数学知识,不难算出答案为:
#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;
}