$$ \newcommand{\seq}[2]{{#1}_{1},{#1}_{2},\cdots,{#1}_{#2}} \newcommand{\num}[1]{1,2,\cdots,#1} \newcommand{\stra}[2]{\begin{bmatrix}#1 \\ #2\end{bmatrix}} \newcommand{\strb}[2]{\begin{Bmatrix}#1 \\ #2\end{Bmatrix}} \newcommand{\dw}[1]{\underline{#1}} \newcommand{\up}[1]{\overline{#1}} $$

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;
}
posted @ 2018-11-07 18:04  chc_1234567890  阅读(244)  评论(0编辑  收藏  举报