两场比赛的第二场:北京师范大学第十六届程序设计竞赛决赛-重现赛
队伍出题:8/11
Rank:8
个人出题:4
团队间的相互配合、相互信任十分重要。越来越能感受到团队竞赛的魅力了。
C 萌萌哒身高差:n个人站成一排,身高为1到n,求一个排列中相邻两个人的身高差的总和的数学期望。
枚举了一下前五项,惊喜的发现答案就是(n*n-1)/3。然后printf就WA了,cout就A了~
代码:
#include<bits/stdc++.h> using namespace std; int q; int t; int main() { long long aa,n; int i; long double kk=3.0; cin>>t; { while(t--){ cin>>n; long double ans=(long double)((n*n)-1)/kk; cout<<fixed<<setprecision(15)<<ans<<endl; } } return 0; }
D 雷电爆裂之力:四条平行路间隔为1米,每两条之间有若干座桥,每座桥的坐标位置给你,让你求最短的从第一条路到第四条路的时间。
我们只需要枚举第二座桥的位置,然后求第一、三座桥在它前、后离他最近的那座桥的距离,四种方案必有一种最优,也就是最后的答案。最后别忘了+3。。。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mo=1e9; const int maxn=100010; ll a[maxn],c[maxn],b[maxn],us[maxn]; int flag; ll tmp,ans,cnt; int n,m,x,y,z,l,bb,t,f,k,xx,yy; ll min(ll x,ll y) { return x>y?y:x; } ll jdz(ll x,ll y) { if(x>y) return x-y; else return y-x; } int main(){ //freopen("input.txt","r",stdin); //freopen("output.txt","w",stdout); int T,cas; cin>>T; while(T--) { cin>>n>>m>>k; for(int i=1;i<=n;i++) { cin>>a[i]; a[i]+=mo; } for(int i=1;i<=m;i++) { cin>>b[i]; b[i]+=mo; } for(int i=1;i<=k;i++) { cin>>c[i]; c[i]+=mo; } a[0]=c[0]=b[0]=(ll)-100010*mo; a[n+1]=b[m+1]=c[k+1]=(ll)100010*mo; sort(a+1,a+n+1); sort(b+1,b+m+1); sort(c+1,c+k+1); ll ans=(ll)100010*mo; int u=1,v=1; ll cnt; for(int i=1;i<=m;i++) { ll tmp=b[i]; while(u<=n&&a[u]<=tmp) u++; while(v<=k&&c[v]<=tmp) v++; cnt=jdz(tmp,a[u-1])+jdz(tmp,c[v-1]); ans=min(ans,cnt); cnt=jdz(tmp,a[u])+jdz(tmp,c[v-1]); ans=min(ans,cnt); cnt=jdz(tmp,a[u-1])+jdz(tmp,c[v]); ans=min(ans,cnt); cnt=jdz(tmp,a[u])+jdz(tmp,c[v]); ans=min(ans,cnt); } cout<<ans+3<<endl; //if(flag) puts("YES");else puts("NO"); } return 0; }
E 可以来拯救吗:给一个长为n的序列,求所有长为k的子序列的和的平方的异或和。
看似复杂,世实际上dfs就可以。由于子序列数量不会超过1e5,所以我们直接用dfs搜出所有的子序列求一下就行。
关键在于组合数的一个性质:C(n,k)=C(n,n-k)。由于子序列数量限制,k必然接近0或者接近n。接近0的话直接暴力即可,接近n的话就转化为n-k,然后记录一下总和sum,求的时候用sum减去就可以了。(相当于取含n-k个数的子序列的和,于是sum-sum(n-k)==sum[k])。这道题是队友想出来的,我只不过是个键盘手。。。所以团队配合多么重要。
代码:
#include <bits/stdc++.h> #define ll unsigned long long using namespace std; const ll mo=1e9*1e9; const int maxn=100010; ll a[maxn]; int us[maxn]; int flag; ll tmp,ans,cnt; int n,m,x,y,z,l,bb,t,f,k,xx,yy; void dfs(int num,ll sum,int cnt) { if(cnt==k){ if(!flag)ans=ans^(sum*sum); else ans=ans^((tmp-sum)*(tmp-sum)); return;} if(num>n) return; dfs(num+1,sum+a[num],cnt+1); dfs(num+1,sum,cnt); } int main(){ //freopen("input.txt","r",stdin); //freopen("output.txt","w",stdout); int T,cas; cin>>T; while(T--) { flag=0; cin>>n>>k; if(k>n/2) {flag=1;k=n-k;} tmp=0; for(int i=1;i<=n;i++) { cin>>a[i]; tmp+=a[i]; } ans=0; dfs(1,0,0); cout<<ans<<endl; //if(flag) puts("YES");else puts("NO"); } return 0; }
F 汤圆防漏理论:给你n个汤圆,每个汤圆i附近有其他汤圆会产生粘度nian[i],如果一个汤圆的粘度大于其硬度的时候你把它夹起来吃,那么它就会漏馅。所有汤圆只能煮成一个硬度。为了防漏,我们求一个最小的硬度,能存在一种合理的方案吃掉所有的汤圆。
比赛结束后,发现大部分大佬都是直接贪心+set过得。。。我是用topo排序+二分过得,时间复杂度nlogn,但是代码感觉比set好写多了。。。
就是把每个点的度数看做粘度,当粘度小于等于硬度的时候即可夹起来吃,然后更新其周围点的粘度即可。。。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mo=1e9*1e9; const int maxn=100010; int c[maxn],he[maxn]; ll in[maxn],du[maxn]; int us[maxn]; int flag; int tmp,ans,cnt; int n,m,x,y,z,l,bb,t,f,k,xx,yy; struct node { int v; int nex; ll w; }a[maxn*4]; void add(int u,int v,ll w) { a[f].v=v; a[f].w=w; a[f].nex=he[u]; he[u]=f++; } int topo(ll k) { queue<int>q; int sum=0; for(int i=1;i<=n;i++) { in[i]=du[i]; if(in[i]<=k) { q.push(i);in[i]=mo;sum++;} } while(!q.empty()) { int t=q.front();q.pop(); for(int i=he[t];i!=-1;i=a[i].nex) { int v=a[i].v; if(in[v]==mo) continue; in[v]-=a[i].w; if(in[v]<=k) { q.push(v);in[v]=mo;sum++;} } } // cout<<(sum==n)<<endl; return (sum==n); } void init() { f=0; memset(he,-1,sizeof(he)); //memset(in,0,sizeof(in)); memset(du,0,sizeof(du)); } int main(){ //freopen("input.txt","r",stdin); //freopen("output.txt","w",stdout); int T,cas; cin>>T; while(T--) { cin>>n>>m; init(); for(int i=1;i<=m;i++) { int x,y; ll z; cin>>x>>y>>z; add(x,y,z); add(y,x,z); du[x]+=z; du[y]+=z; } ll anss=-1; ll l=0,r=(mo/1000000+1); //cout<<mo<<" "<<r<<endl; while(l<r) { ll mid=(l+r)/2; if(topo(mid)) { anss=mid; r=mid; } else l=mid+1; } cout<<anss<<endl; //if(flag) puts("YES");else puts("NO"); } return 0; }
另外K题想到了二维dp且与奇偶项有关,但是可能状态转移方程有误,WA了,有待补题。
要是省赛的题目也能这么简单就好了~
还有8天省赛,继续加油!!!