zoukankan      html  css  js  c++  java
  • 20170723离线赛 · nanhan27's Blog

    引言:感觉到自己的脑洞确实变大了那么一丢丢。

    A、插队——原创By jiedai and nudun

    关键字:单调栈,线段树,树状数组

    【分析】

    题目就是求出每一个点向左数第二个大于它的点的位置。

    首先用一个单调递减的单调栈就能求出向左数第一个大于它的点。接下来就比较麻烦了。

    如何求出第二个呢?难道再暴力往前for吗?

    我的做法仍然是单调栈。对于每一个点,我们都可以知道第一次插队会插到谁那。既然这样,对于一个点,那些第一次插到它的点的关系又会是怎样的?一定是单调递增的!,而且,这些点当然比这个点小。这样按照pos从小到大的顺序在单调栈中查询,不会将前面的有效信息遗漏。

    当然这种是比较难想的,大多数人都是用线段树求出第二个大于它的位置。

    还有一种树状数组的写法,只是写起来比较麻烦,思路也没有线段树简洁,所以并没有人去写。

    B、比赛——CODECHEF SEPT16

    关键字:树形dp、倍增

    【分析】

    比较容易看出是树形dp,但是这个dp比较奇怪,dp转移是从某节点的祖先那里转移过来的。

    快速敲完这题就去写第三题了。然后这题就爆炸了。

    定义dp[t][u]表示u点往根的路径上$2^t$个点中答案的最小值。那么u点的最小花费就是:

    $ans[u]=min(dp[t][F[d-1]],dp[t][F[d-1-k+(1<<t)]])+v)$

    转移就比较简单了:

    $dp[i][u]=min(dp[i-1][u],dp[i-1][F[d-(1<<i-1)]])$

    复杂度$O(n*log(n))$。

    【代码】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    using namespace std;
    #define debug(x) cout<<#x<<" = "<<x<<endl;
    #define ll long long
    #define M 200005
    #define S 20
    struct {int k,v;};
    ll ans[M],dp[S][M];
    vector<int>e[M];
    vector<MP>mp[M];
    int n,m,q;
    int F[M];
    void chk(ll &x,ll y){
    if(x>y)x=y;
    }
    void dfs(int u,int f,int d){
    F[d]=u;
    for(int i=0;u!=1&&i<mp[u].size();i++){
    int k=min(mp[u][i].k,d-1);
    int v=mp[u][i].v;
    int t=0;
    while((1<<t+1)<=k)t++;
    chk(ans[u],min(dp[t][F[d-1]],dp[t][F[d-1-k+(1<<t)]])+v);
    }
    dp[0][u]=ans[u];
    for(int i=1;(1<<i)<=d;i++){
    dp[i][u]=min(dp[i-1][u],dp[i-1][F[d-(1<<i-1)]]);
    }
    for(int i=0;i<e[u].size();i++){
    int v=e[u][i];
    if(v!=f)dfs(v,u,d+1);
    }
    }
    int main(){
    memset(ans,63,sizeof ans);
    scanf("%d %d %d",&n,&m,&q);
    for(int i=1;i<n;i++){
    int u,v;
    e[u].push_back(v);
    e[v].push_back(u);
    }
    while(m--){
    int a,b,c;
    scanf("%d %d %d",&a,&b,&c);
    mp[a].push_back((MP){b,c});
    }
    ans[1]=0;
    dfs(1,0,1);
    while(q--){
    int u;
    scanf("%d",&u);
    printf("%lldn",ans[u]);
    }
    return 0;
    }

    C、采油区域——APIO2009

    关键字:dp,前缀和

    【分析】

    首先我们是做过两块区域的。记得当时的思路就是枚举一条将整个区域分成两块的线。

    现在是分成三块区域。分成三块区域需要两条线,所以复杂度应该是$O(n^2)$,对于这道题应该是能过的。

    三块区域的分布情况有6(4+2)种,而解决这些情况需要5种前缀信息,分别是普通的sum和左上、左下、右上、右下角边长为K的区域石油最多是多少。

    接下来就是用sum来$O(n^2)$预处理出其他4个前缀信息,再用$O(n^2)$来从6种情况中更新答案了。

    所以这道题对我有点启发,对于一个较难的题目,我们可以先弱化一些条件,在想出解决小问题后试着将同样的思路去解决大问题。

    【代码】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #define debug(x) cout<<#x<<" = "<<x<<endl;
    #define rep(a,b,c) for(int a=(b);a<=(c);a++)
    #define per(a,b,c) for(int a=(c);a>=(b);a--)
    #define ll long long
    using namespace std;
    #define M 1505
    int n,m,K,ans;
    int sum[M][M];
    int a[M][M];
    int b[M][M];//右上
    int c[M][M];//左下
    int d[M][M];//右下
    void chk(int &x,int y){
    if(x<y)x=y;
    }
    void Init(){
    per(i,K,n)per(j,K,m)sum[i][j]=sum[i][j]-sum[i-K][j]-sum[i][j-K]+sum[i-K][j-K];
    rep(i,K,n)rep(j,K,m)a[i][j]=max(sum[i][j],max(a[i-1][j],a[i][j-1]));
    rep(i,K,n)per(j,K,m)b[i][j]=max(sum[i][j],max(b[i-1][j],b[i][j+1]));
    per(i,K,n)rep(j,K,m)c[i][j]=max(sum[i][j],max(c[i+1][j],c[i][j-1]));
    per(i,K,n)per(j,K,m)d[i][j]=max(sum[i][j],max(d[i+1][j],d[i][j+1]));
    }
    int main(){
    scanf("%d %d %d",&n,&m,&K);
    rep(i,1,n)rep(j,1,m){
    int x;
    scanf("%d",&x);
    sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
    }
    Init();
    rep(i,K,n-K)rep(j,K,m-K)chk(ans,a[i][j]+b[i][j+K]+c[i+K][m]);
    rep(i,K,n-K)rep(j,K+K,m)chk(ans,a[n][j-K]+b[i][j]+d[i+K][j]);
    rep(i,2*K,n)rep(j,K,m-K)chk(ans,a[i-K][m]+c[i][j]+d[i][j+K]);
    rep(i,K,n-K)rep(j,K,m-K)chk(ans,a[i][j]+b[n][j+K]+c[i+K][j]);
    rep(i,K,n)rep(j,K+K,m-K)chk(ans,sum[i][j]+a[n][j-K]+b[n][j+K]);
    rep(i,2*K,n-K)rep(j,K,m)chk(ans,sum[i][j]+a[i-K][m]+c[i+K][m]);
    printf("%dn",ans);
    return 0;
    }
  • 相关阅读:
    利用google kaptcha完成登陆动态验证码
    Linux和Windows环境下安装Nginx
    将图片设定为固定大小
    将xls格式的Excel转换成图片
    将pdf格式的文件转换成图片
    SSH连接工具类
    java判断ip内外网
    jenkins安装与使用
    Eclipse快捷键大全
    serialVersionUID的作用
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12432943.html
Copyright © 2011-2022 走看看