2018CCPC网络赛-线段树求区间最值+离散化+dp

本文介绍了一种解决2018CCPC网络赛中一道关于最大收益路径问题的方法,通过线段树求区间最值、离散化和动态规划技术,详细阐述了解题思路和代码实现。

转自:HDU6447 YJJ's Salesman-2018CCPC网络赛-线段树求区间最值+离散化+dp

Problem:Portal传送门

 原题目描述在最下面。
 1e5个点,问从(0,0)走到(1e9,1e9)的最大收益。
 当你从(u-1,v-1)走到(u,v)时,你可以获得点(u,v)的权值。

solution:

这里写图片描述
 十分详细了。
 直接线段树区间最值。当然也可以树状数组,不能st表。
 dp[i]=max(query_max(0,dp[i]−1,1)+val[i],dp[i])dp[i]=max(query_max(0,dp[i]−1,1)+val[i],dp[i])
 update(i,dp[i],1)update(i,dp[i],1)
 记得离散化再排序,先x从小到大,再y从大到小,每次更行一行。
 按01背包的更新顺序,滚动数组优化为一维。
 细节见代码。

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define mme(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef unsigned long long LL;
const int N = 2e5 + 7;
const int M = 1e5 + 7;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int ar[N],br[N];//离散化
int dp[N];
struct lh //储存1e5个点
{
    int x,y,v;
} op[N];
bool cmp(lh &a,lh &b)
{
    if(a.x!=b.x)return a.x<b.x;
    return a.y>b.y;
}
int n;
/**********线段树区间最值**********/
struct lp
{
    int l, r, sum;
} cw[N<<2];
void push_up(int rt)
{
    cw[rt].sum = max(cw[lson].sum, cw[rson].sum);
}
void build(int l,int r,int rt)
{
    cw[rt].l = l;
    cw[rt].r = r;
    cw[rt].sum = 0;
    if(l==r)
    {
        return;
    }
    int m = (l+r)/2;
    build(l,m,lson);
    build(m+1,r,rson);
    push_up(rt);
}
void update(int p,int c,int rt)
{
    int l = cw[rt].l;
    int r = cw[rt].r;
    int m = (l+r)/2;
    if(l==r)
    {
        cw[rt].sum = c;
        return;
    }
    if(p<=m)update(p,c,lson);
    else update(p,c,rson);
    push_up(rt);
}
int query(int L,int R,int rt)
{
    int l = cw[rt].l;
    int r = cw[rt].r;
    int m = (l+r)/2;
    if(L<=l&&r<=R)
    {
        return cw[rt].sum;
    }
    if(L>m)return query(L,R,rson);
    else if(R<=m)return query(L,R,lson);
    return max(query(L,m,lson),query(m+1,R,rson));
}
/****************/
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 0; i < n; ++i)
        {
            scanf("%d%d%d",&op[i].x,&op[i].y,&op[i].v);
            ar[i] = op[i].x;
            br[i]=op[i].y;
        }
        int p = n + 1;
        ar[n] = 0;
        br[n] = 0;
        sort(ar,ar+p);
        sort(br,br+p);
        int a = unique(ar,ar+p)-ar;
        int b = unique(br,br+p)-br;
        for(int i = 0; i < n; ++i)
        {
            op[i].x=lower_bound(ar,ar+a,op[i].x)-ar;
            op[i].y=lower_bound(br,br+b,op[i].y)-br;
        }
        //以上离散化
        sort(op,op+n,cmp);
        build(0, b, 1);
        mme(dp, 0);
        for(int i = 0; i < n; ++i)
        {
            int flag = op[i].x, j;
            for(j = i; j < n; ++j)
            {
                if(op[j].x != flag)
                {
                    break;
                }
                int tmp = query(0, op[j].y-1, 1) + op[j].v;
                if(tmp > dp[op[j].y])
                {
                    dp[op[j].y] = tmp;
                    update(op[j].y, dp[op[j].y], 1);
                }
            }
            i = j - 1;
        }
        int ans = 0;
        for(int i = 0; i <= b; ++i)
        {
            ans = max(ans, dp[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

不同年份的CCPC网络有不同的题目及解答: - 2018CCPC网络:给一张竞图,对于所有的4元组,若为四元环贡献是1,有两个点的出度是2则贡献是 -1。先假设所有四元组都是四元环,这部分对答案的贡献是A(n,4)。对于非四元环的四元组,必然存在一个点出度是2,枚举每个度数>=2的点,其对答案的贡献是A(deg[i],2)*(n - 3)*4 ,所以终答案ans = A(n,4) - ΣA(deg[i],2)*(n - 3)*4 ,这其实要ΣC(deg[i],2),C(deg,2)=deg*(deg - 1)/2 ,当前度数为n,度数每增加1,对于这个点贡献增加n,可暴力连边跑费用流解[^2]。 - 2021年CCPC网络:发现规律,先将剩余的1或2或3构造出来,剩下的四个一组构造。代码实现如下: ```cpp #include<bits/stdc++.h> using namespace std; int main(void) { int t; cin>>t; while(t--) { int n; cin>>n; string ans; if(n%4==1) n=n-1,ans+="1"; else if(n%4==2) n=n-2,ans+="0001"; else if(n%4==3) n=n-3,ans+="01"; for(int i=1;i<=n/4;i++) ans+="1001"; cout<<ans.size()<<endl; cout<<ans<<endl; } return 0; } ``` 该题1009为Command Sequence,难度一般,知识点涉及前缀和与哈希[^4]。 - 2016年CCPC网络:一个星球一年有73天,另一个一年有137天,给定第n天,判断这一天是否同时是两个星球一年中的第一天,即判断n是否同时整除73和137,因n超大,使用紫书的大数模去一个较小的数的方法,代码如下: ```cpp #include <cstdio> #include <string> #include <cstring> #include <iostream> using namespace std; char n[10000010]; int m; int main(){ int m = 10001; int cas = 1; while(~scanf("%s",n)){ int len = strlen(n); int ans = 0; for(int i=0;i < len;i++){ ans = (int)(((long long)ans*10+n[i]-'0')%m); } printf("Case #%d: ",cas++); if (ans == 0) printf("YES\n"); else printf("NO\n"); } return 0; } ``` [^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值