2019牛客多校训练(二)
比赛链接:
https://ac.nowcoder.com/acm/contest/882#question
A.Eddy Walker
题意:
在一个$n$个节点的环中,一个人随机向前或者向后,求出他刚好走过所有节点时,位于$m$点的概率
分析:
对于$n$等于18,我们可以用随机数模拟移动的方向,记录以$i$点结束的次数
发现,除了0点不可能结束,其它点结束的概率相同,那么概率就只能是$\frac{1}{n-1}$
ac代码:
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<int,int> using namespace std; const int maxn=14+5; const int maxm=1e7+10; const ll mod=1e9+7; ll qpow(ll x,ll y) { ll res=1,k=x; while(y) { if(y%2)res=res*k%mod; k=k*k%mod; y/=2; } return res; } int main() { //cout<<qpow(2,10)<<endl; int T,n,m; scanf("%d",&T); ll ans=1; while(T--) { scanf("%d %d",&n,&m); if(n==1&&m==0) ; else if(n!=0&&m==0) ans=0; else ans=ans*qpow((n-1),mod-2)%mod; printf("%lld\n",ans); } return 0; }
F.Partition problem
题意:
总共有$2n$个人,分成两队
如果a,b不在一队那么竞争值+$V_{ab}$
求最大竞争值
分析:
用dfs暴力搜索在$2n$人里面选择$n$个人的所有情况,注意每次加人和减人都去除或添加点的影响,而不要等在搜索到结果再执行这个操作
这样,复杂度从$C(2n,n)n^{2}$降到了$C(2n,n)n$,因为给了四秒,刚好可以跑过去
ac代码:
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<int,int> using namespace std; const int maxn=14+5; const int maxm=1e7+10; const ll mod=998244353; int num[maxn],ma[maxn*2][maxn*2],n; ll v[maxn*2],ans; bool vis[maxn*2]; void dfs(int s,int f) { if(s-f>n)return ; if(vis[s]) { vis[s]=false; for(int j=1;j<=2*n;j++)v[j]-=ma[s][j]; } num[f]=s; if(f==n) { ll res=0; for(int i=1;i<=n;i++) res+=v[num[i]]; // for(int i=1;i<=2*n;i++) // cout<<vis[i]<<" "; // cout<<endl; ans=max(ans,res); // cout<<res<<endl; return ; } for(int i=s+1;i<=2*n;i++) { dfs(i,f+1); if(vis[i]==false) { vis[i]=1; for(int j=1;j<=2*n;j++)v[j]+=ma[i][j]; } } } int main() { scanf("%d",&n); for(int i=1;i<=2*n;i++) for(int j=1;j<=2*n;j++) scanf("%d",&ma[i][j]); for(int i=1;i<=2*n;i++)vis[i]=true; for(int i=1;i<=2*n;i++)for(int j=1;j<=2*n;j++)v[i]+=ma[i][j]; for(int i=1;i<=2*n;i++) { dfs(i,1); if(vis[i]==false) { vis[i]=true; for(int j=1;j<=2*n;j++)v[j]+=ma[i][j]; } } printf("%lld\n",ans); return 0; }
H.Second Large Rectangle
题意:
在01矩阵中求出第二大的全1矩阵,只需要输出矩阵大小
分析:
用单调栈求出以第$i$行为底的所有极大全1矩阵,把原矩阵和少一列的矩阵放入结果集,每次保留两个结果
两次单调栈好写,其实一次单调栈也可以求出解
ac代码:
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<int,int> using namespace std; const int maxn=1e3+5; const int maxm=1e7+10; const ll mod=998244353; char word[maxn][maxn]; int num[maxn][maxn]; int zz[maxn]; int n,m,top,l[maxn],r[maxn]; pa sk[maxn]; int ans[3]; bool vis[maxn][maxn]; void add(int x) { ans[0]=x; sort(ans,ans+3); } void work(int x,int y) { int v=l[y],w=r[y]; if(vis[v][w])return ; vis[v][w]=true; add((w-v)*num[x][y]); add((w-v+1)*num[x][y]); } int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++)scanf("%s",word[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(word[i][j]=='1')num[i][j]=num[i-1][j]+1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { while(top&&sk[top].first>num[i][j]) { r[sk[top].second]=j-1; top--; } sk[++top]=make_pair(num[i][j],j); } while(top)r[sk[top].second]=m,top--; for(int j=m;j>=1;j--) { while(top&&sk[top].first>num[i][j]) { l[sk[top].second]=j+1; top--; } sk[++top]=make_pair(num[i][j],j); } while(top)l[sk[top].second]=1,top--; for(int j=1;j<=m;j++)work(i,j); for(int j=1;j<=m;j++)vis[l[j]][r[j]]=false,l[j]=r[j]=0; } printf("%d\n",ans[1]); return 0; }