两场比赛的第二场:北京师范大学第十六届程序设计竞赛决赛-重现赛
队伍出题: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天省赛,继续加油!!!