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(nlog n))

统计前缀和。
本题标程的复杂度是(O(n^2log 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(nlog 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))
的有根树。猫想要把树上所有 ( ext{LCA}) 抓下来,但是他答应,只要你能回答他的一个问题,他就放过你的苹果树。
猫会给你一个长度为 (n)(1)(n) 的排列 (p),定义连续子段 (p[l, r]) 的权值如下:

[val[l, r] = depth[lca(pl, pl+1, . . . , pr)]$$ 也就是 $pl, pl+1, . . . , pr$ 的 $ ext{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$ ## 分析 观察样例,我们先把相邻点的 $ ext{LCA}$ 求出: 节点 1 2 3 4 5 6 $ ext{LCA}$ 1 2 3 6 6 设求出的序列为 $l$ ,则 $l_i$ 表示 $ ext{LCA}_{a_i,a_{i+1}}$ 。$dep$ 表示节点深度序列。 对于其中任意三个连续节点 $a_i,a_{i+1},a_{i+2}$ ,我们发现它们的 $ ext{LCA}$ 就是 $$dep[ ext{LCA}_{a_i,a_{i+1}}]<dep[ ext{LCA}_{a_{i+1},a_{i+2}}];?; ext{LCA}_{a_i,a_{i+1}}: ext{LCA}_{a_{i+1},a_{i+2}}]

同样,对于任意的 (k) 个连续节点,它们的 ( ext{LCA}) 也可以用类似的方法求出。
于是,我们枚举每一个 (l_i) ,算出它左边第一个深度小于它的节点 (l_j) 和右边第一个深度小于它的节点 (l_k) ,则以 (l_i)( ext{LCA}) 的所有区间的答案为 ((l_i-l_j+1)*(l_k-l_i+1)*dep[l_i])
我没发现上面的问题其实就是求一个最大子矩形,用单调栈或其他一些方法 (O(n)) 求解即可。
总时间复杂度 (O(nlog n)),瓶颈在求 ( ext{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