2018年4月28日训练日记(第二场比赛)

两场比赛的第二场:北京师范大学第十六届程序设计竞赛决赛-重现赛

队伍出题: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天省赛,继续加油!!!

猜你喜欢

转载自blog.csdn.net/lsd20164388/article/details/80138516