2017.11.07离线赛总结

本文详细解析了三个算法问题:旅行问题采用贪心算法优化路径;圣诞树问题通过树状结构计算平均距离;集合问题利用高精度计算寻找特定元素。

travel ——3814

思路:显然的dp,但显然有点贪心的味道。
首先,说一下O(n3)的暴力dp:dp[i][j] 表示走到第i个路段时,上一个路段的高度为j,转移也十分容易,dp[i][j]=min(dp[i1][k]+abs(A[i]k)+abs(kj),dp[i][j])
这里,显然第一维除了空间上可以滚动,时间上是肯定不能优化了,而j的高度范围也很大,这就使人不难去猜想每个路段之间的高度关系。
再仔细思考,一定是在出现峰谷时才使用魔法,也就是满足A[i]>A[i-1]&&A[i]>A[i+1]  ||  A[i]<A[i-1] && A[i]<A[i+1]
那么发现这就是一道贪心题了,每次修改高度,加上高度差或消耗魔法值即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff

#define N 100005

int n;
LL A[N];
struct p40{
    LL dp[405][405];
    void solve(){
        mcl(dp,127);
        REP(i,0,400)dp[0][i]=0;
        REP(i,1,n){
            REP(j,0,400)dp[i][A[i]]=min(dp[i][A[i]],dp[i-1][j]+abs(j-A[i]));
            REP(j,0,400)
                REP(k,0,400)
                    dp[i][k]=min(dp[i][k],dp[i-1][j]+abs(j-k)+abs(A[i]-k));
        }
        LL ans=1e15;
        REP(i,0,400)ans=min(ans,dp[n][i]);
        cout<<ans<<endl;
    }
}p40;
struct p100{
    void solve(){
        LL ans=0;
        REP(i,2,n){
            if(A[i]>A[i-1] && A[i]>A[i+1]){
                ans+=abs(A[i]-max(A[i-1],A[i+1]));
                A[i]=max(A[i-1],A[i+1]);
            }
            else if(A[i]<A[i-1] && A[i]<A[i+1]){
                ans+=abs(A[i]-min(A[i-1],A[i+1]));
                A[i]=min(A[i-1],A[i+1]);
            }
        }
        REP(i,2,n)ans+=abs(A[i]-A[i-1]);
        cout<<ans<<endl;
    }
}p100;
int main(){
//  freopen("travel.in","r",stdin);
//  freopen("travel.out","w",stdout);
    cin>>n;
    REP(i,1,n)scanf("%lld",&A[i]);
    if(n<=400)p40.solve(); 
    else p100.solve();
    return 0;
}

santa ——3815

思路:经典的树上算平均距离,原题是2个点的平均距离,而此题是3个点。
其实也是一样的。一遍dfs算出每个点对边的贡献,最后再处理边数(然而,这里考试时没算好)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define pb push_back
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1

#define N 500005

int n;
struct node{
    int to,cost;
};
vector<node>E[N];

struct p40{
    #define M 1005
    int dis[M][M];
    void solve(){
        mcl(dis,63);
        REP(i,1,n-1){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            dis[a][b]=dis[b][a]=c;
        }   
        REP(k,1,n)
            REP(i,1,n)
                REP(j,1,n)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        db ans=0,cnt=0;
        REP(i,1,n){
            REP(j,i+1,n){
                REP(k,j+1,n){
                    ans+=dis[i][j]+dis[i][k]+dis[k][j];
                    cnt++;  
                }
            }
        }
        printf("%.2lf\n",ans/cnt);
    }
}p40;
struct p100{
    db ans;
    int sz[N];
    LL C[5];
    void dfs(int x,int f){
        sz[x]=1;
        REP(i,0,E[x].size()-1){
            node y=E[x][i];
            if(y.to==f)continue;
            dfs(y.to,x);
            sz[x]+=sz[y.to];
            ans+=1.0*y.cost*sz[y.to]*(n-sz[y.to])*(n-2)/C[3];
        }
    }
    void solve(){
        C[0]=1;
        REP(i,1,3)C[i]=C[i-1]*(n-i+1)/i;

        REP(i,1,n-1){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            E[a].push_back((node){b,c});
            E[b].push_back((node){a,c});
        }
        ans=0;
        dfs(1,0);
        printf("%.2lf\n",ans);
    }
}p100;
int main(){
//  freopen("santa.in","r",stdin);
//  freopen("santa.out","w",stdout);
    cin>>n;
    if(n<3){
        REP(i,1,n-1){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
        }
        printf("%.2lf\n",(db)0);
    }
    else if(n<=100)p40.solve();
    else p100.solve();
    return 0;
}

set ——3816

思路:其实这题不难,真心不难,集合是有规律的,除(n=3外,此时集合的大小恒为3),每次[L,R]一定会变成[2*L+1,2*R-1],那么就可以去找k是否在这个区间,发现这个后,这题就是一个高精度模板题。(贴一下模板…)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define pb push_back
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1

#define P 100000000
#define N 40005

int n,m;
struct Big{
    LL num[N],len;
    Big(){
        memset(num,0,sizeof(num));
        len=1;
    }
    Big operator +(const Big &a)const{
        Big b;
        b.len=max(len,a.len);
        REP(i,0,b.len-1){
            LL &B=b.num[i];
            B+=num[i]+a.num[i];
            if(B>=P)B-=P,b.num[i+1]++;
        } 
        if(b.num[b.len])b.len++;
        return b;
    }
    Big operator -(const Big &a)const{
        Big b;
        REP(i,0,len-1)b.num[i]=num[i];
        b.len=len;
        DREP(i,a.len-1,0){
            b.num[i]-=a.num[i];
            int now=i;
            while(b.num[now]<0)b.num[now]+=P,now++,b.num[now]--;
        }
        while(b.len>1 && !b.num[b.len-1])b.len--;
        return b;
    }
    Big operator *(const Big &a)const{
        Big b;
        b.len=a.len+len-1;
        REP(i,0,len-1){
            REP(j,0,a.len-1){
                LL &B=b.num[i+j];
                B+=num[i]*a.num[j];
                if(B>=P)b.num[i+j+1]+=B/P,B%=P;
            } 
        } 
        while(b.num[b.len])b.len++;
        return b;
    }
    Big operator /(const int &a)const{
        Big b;
        REP(i,0,len-1)b.num[i]=num[i];
        DREP(i,len-1,1)b.num[i-1]+=b.num[i]%a*P,b.num[i]/=a;
        b.num[0]/=a;
        b.len=len;
        while(b.len>1 && !b.num[b.len-1])b.len--;
        return b;
    }
    void Rd(){
        char A[N];      
        scanf("%s",A);
        int SL=strlen(A);
        len=0;
        for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=8){
            if(i>=7)for(int j=i-7;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
            else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
        }
    }
    void Pr(){
        printf("%lld",num[len-1]);
        DREP(i,len-2,0)printf("%08lld",num[i]);     
    }
    void jia(){
        num[0]++;
        REP(i,0,len-1){
            if(num[i]>=P)num[i]-=P,num[i+1]++;
            else break;
        }
        if(num[len])len++;
    }
    void jian(){
        num[0]--;
        REP(i,0,len-1){
            if(num[i]<0)num[i]+=P,num[i+1]--;
            else break;
        }
        while(!num[len-1] && len>0)len--;
    }
    bool operator<=(const Big &a)const{
        if(len!=a.len)return len<a.len;
        DREP(i,len-1,0)
            if(num[i]!=a.num[i])return num[i]<a.num[i];
        return 1;
    }
};
struct p3{
    void solve(){
        Big a,ans;
        a.num[0]=2;
        ans.num[0]=1;
        scanf("%d",&m);
        int b=(m-1)/3+1;
        while(b){
            if(b&1)ans=ans*a;
            b>>=1;
            if(!b)break;
            a=a*a;
        }
        if(m%3==1)ans.jian();
        else if(m%3==0)ans.jia();
        ans.Pr();
    }
}p3;
struct p100{
    void solve(){
        Big L,R,k,sum,ans;
        k.Rd();
        L.num[0]=1;
        R.num[0]=n;
        sum=R-L;
        sum.jia();
        if(k<=sum){
            ans=L+k;
            ans.jian();
            ans.Pr();
        }
        else {
            k=k-sum;
            while(1){
                L=L+L;R=R+R;
                L.jia();R.jian();
                sum=R-L;
                sum.jia();
                if(k<=sum)break;
                else k=k-sum;
            } 
            ans=L+k;
            ans.jian();
            ans.Pr();
        }
    }
}p100;
int main(){
//  freopen("set.in","r",stdin);
//  freopen("set.out","w",stdout);
    scanf("%d",&n);
    if(n<3){
        scanf("%d",&m);
        if(n==1){
            if(m==1)puts("1");
            else puts("-1");
        } 
        else if(n==2){
            if(m==1)puts("1");
            else if(m==2)puts("2");
            else if(m==3)puts("3");
            else puts("-1");
        }
    }
    else if(n==3)p3.solve();
    else p100.solve();
    return 0;
}

小结:今天的考试题目真心不难,会想一下,考试时都想得不深入,很浅,而且还是有点急了(当时第2题暴力folyd迷之打错,就很慌,无奈写了lca…(给小c喷死)),最后竟然还有时间一直在检查第1题…——时间还是要看题目而分配呀。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值