xj膜你赛(n-1)

Strategy

1.1 Background

针针喜欢玩一款叫做 DotA (Defense of the Algorithm) 的游戏。——CTSC2018 Day1 T1 假面

1.2 Description

继 8102CSTC 之后,DotA 推出了新的游戏模式。在一场对战中,针针需要面对 \(n(1 ≤n ≤ 4000)\) 个敌人,对于第 i 个敌人针针有如下技能可以选择 (必须选择其中一种):

  • 花费 \(attack_i\) 的代价主动进攻,迫使敌人进入防御状态,使之无法进攻,但是在一场对战中只能攻击 \(k\) 次。
  • 花费 \(defend_i\) 的代价防御防御该敌人的进攻,该技能可以在对战中使用任意多次。
  • 与该敌人结盟。注意,结盟只能对 1 个敌人使用,此时既不能攻击该敌人,也不必防御该敌人的进攻。

现在给定每个敌人的 \(attack\)\(defend\) , 以及最多攻击次数 \(k\), 针针想要知道, 在和第 \(i\) 个敌人结盟的情况下, 他在这一轮对战中最少花费的代价。

1.3 Input Format

第一行一个正整数 \(n(1 ≤ n ≤ 4000)\),表示敌人个数。
接下来 \(n\) 行,每行两个正整数 \(attack_i(1 ≤ attack_i ≤ 10^9)\)\(defend_i(1 ≤ defend_i ≤ 10^9)\)

1.4 Output Format

一行 \(n\) 个整数, 第 \(i\) 个数表示和第 \(i\) 个敌人结盟时的最小代价。

1.5 Sample Input

4
1 5
2 3
2 4
3 5

1.6 Sample Output

8 8 7 6

1.7 Guarantee

\(n ≤ 4000\)

分析

贪心题。

1.\(O(n^2)\)

先设所有的全部选 \(attack\) ,再暴力枚举最优方案即可。

2.\(O(n\log n)\)

统计前缀和。
本题标程的复杂度是\(O(n^2\log n)\)……

Code

1.\(O(n^2)\)

#include<cstdio>
#include<algorithm>
#define maxn 40002
using namespace std;
template<typename tp>
tp read(){
    char c=getchar();
    bool sgn=0;
    tp x=0;
    while((c<'0'||c>'9')&&c!='-')c=getchar();
    if(c=='-')sgn=1,c=getchar();
    while(c>='0'&&c<='9'){
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return sgn?-x:x;
}
template<typename tp>
void write(const tp x){
    if(x<0)putchar('-'),write(-x);
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
int n,k;
long long a[maxn],sum;
struct data{
    long long val;
    int pos;
    bool operator <(const data& dat)const{
        return val<dat.val;
    }
}d[maxn];
int main(){
    n=read<int>(),k=read<int>();
    for(int i=1;i<=n;i++){
        a[i]=read<long long>();
        d[i].val=read<long long>()-a[i];
        d[i].pos=i;
        sum+=a[i];
    }
    sort(d+1,d+n+1);
    for(int i=1;i<=n;i++){
        long long res=sum-a[i];
        int j=1,cnt=1;
        for(;cnt<n-k;j++){
            if(d[j].pos!=i)res+=d[j].val,cnt++;
        }
        for(;j<=n;j++){
            if(d[j].pos!=i){
                if(d[j].val<0)res+=d[j].val;
                else break;
            }
        }
        printf("%lld ",res);
    }
    return 0;
}

2.\(O(n\log n)\)

#include<cstdio>
#include<algorithm>
#define maxn 4005
#define INF 100000000000000000ll
using namespace std;
long long read(){
    char c=getchar();
    bool sgn=0;
    long long x=0;
    while((c<'0'||c>'9')&&c!='-')c=getchar();
    if(c=='-')sgn=1,c=getchar();
    while(c>='0'&&c<='9'){
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return sgn?-x:x;
}
void write(const long long x){
    if(x<0)putchar('-'),write(-x);
    else{
        if(x>=10)write(x/10);
        putchar(x%10+'0');
    }
}
struct node{
    long long val,pos;
}a[maxn];
inline bool cmp(const node& x,const node& y){
    return x.val>y.val;
}
long long n,k,at[maxn],c[maxn],de[maxn],dp[maxn],sumd=0,suma=0;
bool b[maxn];
int main(){
    n=read(),k=read();
    for(int i=1;i<=n;i++){
        at[i]=read(),de[i]=read();
        a[i].val=de[i]-at[i];
        a[i].pos=i;
        c[i]=a[i].val;
        sumd+=de[i];
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=k;i++){
        if(a[i].val>0){
            b[a[i].pos]=1;
            suma+=a[i].val;
        }
    }
    for(int i=1;i<=n;i++){
        long long ans=sumd-de[i];
        if(b[i])ans-=suma-c[i]+(a[k+1].val>0?a[k+1].val:0);
        else ans-=suma;
        write(ans),putchar(' ');
    }
    return 0;
}

Easy LCA (easy.cpp)

2.1 Background

问: 如何评价 WC2018 和 CTSC2018?
曰: 猫喜欢上树找 LCA。
猫: 我这次有备而来。

2.2 Description

猫喜欢上了你家的苹果树。你家的苹果树是一个以 \(1\) 号点为根的,节点数为 \(n(n ≤ 6*10^5)\)
的有根树。猫想要把树上所有 \(\text{LCA}\) 抓下来,但是他答应,只要你能回答他的一个问题,他就放过你的苹果树。
猫会给你一个长度为 \(n\)\(1\)\(n\) 的排列 \(p\),定义连续子段 \(p[l, r]\) 的权值如下:
\[val[l, r] = depth[lca(pl, pl+1, . . . , pr)]\] 也就是 \(pl, pl+1, . . . , pr\)\(\text{LCA}\) 的深度。他希望求出所有 \(n(n+1)/2\) 个连续子段的权值和 (i.e. \(\sum^n_{i=1}\sum^n_{j=i}val[i, j]\) )。根节点深度为 \(1\)
你正要用苹果树出一道题,所以为了防止猫弄坏你精心设计的苹果树,你决定回答这个问题。

2.3 Input Format

第一行一个正整数 \(n(1 ≤ n ≤ 6*10^5)\)
接下来 \(n - 1\) 行,每行两个正整数 \(u_i, v_i(1 ≤ u_i, v_i ≤ n)\) ,代表树上的一条边 \((u, v)\)
数据保证输入的是一棵合法的树。
接下来一行包含 \(n\) 个正整数,代表一个 \(1\)\(n\) 的排列 \(p\)

2.4 Output Format

输出一行一个整数,表示所求的所有连续子段的权值和。

2.5 Sample Input

6
1 2
2 6
6 3
3 4
6 5
1 2 3 4 5 6

2.6 Sample Output

51

2.7 Guarantee

\(n ≤ 6*10^5\)

分析

观察样例,我们先把相邻点的 \(\text{LCA}\) 求出:
节点 1 2 3 4 5 6
\(\text{LCA}\) 1 2 3 6 6
设求出的序列为 \(l\) ,则 \(l_i\) 表示 \(\text{LCA}_{a_i,a_{i+1}}\)\(dep\) 表示节点深度序列。
对于其中任意三个连续节点 \(a_i,a_{i+1},a_{i+2}\) ,我们发现它们的 \(\text{LCA}\) 就是 \[dep[\text{LCA}_{a_i,a_{i+1}}]<dep[\text{LCA}_{a_{i+1},a_{i+2}}]\;?\;\text{LCA}_{a_i,a_{i+1}}:\text{LCA}_{a_{i+1},a_{i+2}}\]
同样,对于任意的 \(k\) 个连续节点,它们的 \(\text{LCA}\) 也可以用类似的方法求出。
于是,我们枚举每一个 \(l_i\) ,算出它左边第一个深度小于它的节点 \(l_j\) 和右边第一个深度小于它的节点 \(l_k\) ,则以 \(l_i\)\(\text{LCA}\) 的所有区间的答案为 \((l_i-l_j+1)*(l_k-l_i+1)*dep[l_i]\)
我没发现上面的问题其实就是求一个最大子矩形,用单调栈或其他一些方法 \(O(n)\) 求解即可。
总时间复杂度 \(O(n\log n)\),瓶颈在求 \(\text{LCA}\) 。空间复杂度 \(O(n)\)

Code

#include<cstdio>
#include<algorithm>
#define maxn 1200010
using namespace std;
namespace FASTIO{
    int read(){
        char c=getchar();
        bool sgn=0;
        int x=0;
        while((c<'0'||c>'9')&&c!='-')c=getchar();
        if(c=='-')sgn=1,c=getchar();
        while(c>='0'&&c<='9'){
            x=(x<<3)+(x<<1)+c-'0';
            c=getchar();
        }
        return sgn?-x:x;
    }
    void write(const long long x){
        if(x<0)putchar('-'),write(-x);
        else{
            if(x>=10)write(x/10);
            putchar(x%10+'0');
        }
    }
}
namespace TREE{
    struct edge{
        int to,next;
    };
    edge e[maxn<<1];
    int head[maxn],cnte;
    void add(int u,int v){
        e[++cnte].to=v;
        e[cnte].next=head[u];
        head[u]=cnte;
    }
    int n;
}
using TREE::n;
namespace LCA{
    int lg[maxn],fa[maxn][30],dep[maxn];
    void initlg(){
        for(int i=1;i<=n;i++)lg[i]=lg[i-1]+((1<<lg[i-1])==i);
    }
    void initdfs(int u,int last){
        dep[u]=dep[last]+1;
        fa[u][0]=last;
        for(int i=1;(1<<i)<dep[u];i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        for(int i=TREE::head[u];i;i=TREE::e[i].next){
            int v=TREE::e[i].to;
            if(v==last)continue;
            initdfs(v,u);
        }
    }
    int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        while(dep[x]>dep[y]){
            x=fa[x][lg[dep[x]-dep[y]]-1];
        }
        if(x==y)return x;
        for(int i=lg[dep[x]];i>=0;i--){
            if(fa[x][i]!=fa[y][i]){
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
}
namespace STACK{
    int l[maxn],top,left[maxn],right[maxn],st[maxn];
    void work1(){
        top=0;
        for(int i=1;i<n;i++){
            while(top&&l[i]<=l[st[top]]){
                right[st[top]]=i-1;
                top--;
            }
            st[++top]=i;
        }
        while(top){
            right[st[top]]=n-1;
            top--;
        }
    }
    void work2(){
        top=0;
        for(int i=n-1;i>=1;i--){
            while(top&&l[i]<l[st[top]]){
                left[st[top]]=i+1;
                top--;
            }
            st[++top]=i;
        }
        while(top){
            left[st[top]]=1;
            top--;
        }
    }
}
int a[maxn];
int main(){
    n=FASTIO::read();
    for(int i=1;i<n;i++){
        int u=FASTIO::read(),v=FASTIO::read();
        TREE::add(u,v);
        TREE::add(v,u);
    }
    for(int i=1;i<=n;i++)a[i]=FASTIO::read();
    LCA::initlg();
    LCA::initdfs(1,0);
    for(int i=1;i<n;i++)STACK::l[i]=LCA::dep[LCA::lca(a[i],a[i+1])];
    STACK::work1();
    STACK::work2();
    long long ans=0;
    for(int i=1;i<n;i++)ans+=(long long)(i-STACK::left[i]+1)*(STACK::right[i]-i+1)*STACK::l[i];
    for(int i=1;i<=n;i++)ans+=LCA::dep[a[i]];
    FASTIO::write(ans);
    return 0;
}

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/9924201.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值