noip2018 10.18 day2

                                                   轻功

Description

题目背景: 尊者神高达进入了基三的世界,作为一个 mmorpg 做任务是必不可少的,然而跑地图却令人十分不爽。好在基三可以使用轻功,但是尊者神高达有些手残,他决定用梅花桩练习轻功。 题目描述: 一共有 n 个木桩,要求从起点(0)开始,经过所有梅花桩,恰好到达终点 n,尊者神高达一共会 k 种门派的轻功,不同门派的轻功经过的梅花桩数不同,花费时间也不同。但是尊者神高达一次只能使用一种轻功,当他使用别的门派的轻功时,需要花费 W 秒切换(开始时可以是任意门派,不需要更换时间)。由于尊者神高达手残,所以经过某些梅花桩(包括起点和终点)时他不能使用一些门派的轻功。尊者神高达想知道他最快多久能到达终点如果无解则输出-1。

Input

第一行 n,k,W 接下来 k 行,每行为 ai 和 wi 代表第 i 种轻功花费 vi 秒经过 ai 个木桩。 接下来一行 Q 为限制条件数量。 接下来 Q 行,每行为 xi 和 ki 代表第 xi 个梅花桩不能使用第 ki 种门派的轻功经过。

Output

一行答案即所需最短时间。

Sample Input

Sample Input1:

6 2 5

1 1

3 10

2

1 1

2 1

Sample Input2:

6 2 5

1 1

3 10

0

Sample Output

Sample Output1:

18

样例解释 1: 先用第二种轻功花费 10 秒到 3,再用 5 秒切换到第一种轻功,最后再用 3 秒时间到 6.一共花费 10+5+3=18 秒

Sample Output2:

6

样例解释 2:

直接花费 6 秒到 6;

Data Constraint

20%的数据 n<=20,k<=10,Q<=200;

对于另外 20%的数据 W=0

对于另外 20%的数据 Q=0

所以数据满足 n<=500,k<=100,Q<=50000,vi<=1e7;

保证数据合法

Hint

Q:请问第一题可不可以往回跳

A:不可以

今天只改出来一道题的正解orz,就是这道简单DP。后面俩题都要用树链剖分,不会,于是今天学了,过了模板题,但后面两道题思路还转不过来。。

那么这道明显的DP题看题很容易想到状态为f[i][j]代表走到第i个木桩,当前要用第j种轻功的情况。

那么枚举上一次用的功法k,两种情况:

  if(j == k)  f[i][j] = min(f[i][j],f[i-a[k][k] + v[k])

else f[i][j] = min(f[i][j],f[i-a[k][k] + v[k] + w)

其中w如题所述,v[k]是k功法跳一次所需时间,a[k]代表k功法一次能跳过的柱子数。

答案为f[n][i](1 <= i <= m)

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt(x) putchar(x)
const int INF = 0x3f3f3f3f;
const int MAXN = 5005;
const int MAXQ = 5e4 + 5;
int n,m,w,q,a[MAXN],v[MAXN];
ll f[MAXN][MAXN];
int sum[MAXN][MAXN];
int no[MAXQ];
ll ans = INF;
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

inline void init()
{
	in(n); in(m); in(w);
	for(int i = 1;i <= m;i++) in(a[i]),in(v[i]);
	in(q);
	for(int i = 1;i <= q;i++) 
	{
		int x,k; in(x); in(k);
		no[x] = k; sum[x][k]++;
	}
	for(int i = 1;i <= m;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			sum[j][i] += sum[j-1][i];
		}
	}
}

bool check(int l,int r,int kind)
{
//	for(int i = now+1;i <= now+a[kind];i++)
//		if(no[i] == kind) return 0;
//	if(sum[r][kind] - sum[l][kind] == 0) return 1;
	ll num = sum[r][kind];
	if(l) num -= sum[l-1][kind];
	return (num == 0);
}

int main()
{
	init();
	memset(f,0x3f,sizeof f);
	for(int i = 1;i <= m;i++) f[0][i] = 0;
	for(int i = 0;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
			for(int k = 1;k <= m;k++)
				if(i - a[k] >= 0)
				{
					if(check(i-a[k],i,k))
					{
						if(k == j) f[i][j] = min(f[i][j],f[i-a[k]][k]+v[k]);
						else f[i][j] = min(f[i][j],f[i-a[k]][k] + v[k] + w);
					}
				}
	}
	for(int i = 1;i <= m;i++) ans = min(ans,f[n][i]);
	if(ans == INF) out(-1);
	else out(ans);
	return 0;
}

开荒

Description

题目背景: 尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门。他加入师门后发现有无穷无尽的师兄弟姐妹,这几天新副本开了,尊者神高达的师门作为一个 pve师门,于是他们决定组织一起去开荒。

题目描述: 师门可以看做以 1 为根的一棵树,师门中的每一个人都有一定的装备分数。一共会有 q 个事件。每个事件可能是一次开荒,也可能是因为开荒出了好装备而导致一个人的装分出现了变化。对于一次开荒,会有 k 个人组织,由于师门的号召力很强,所以所有在组织者中任意两个人简单路径上的人都会参加。

Input

第一行 n ,q; 接下来 1 行 n 个数,代表每个人的分值; 接下来 n-1 行 u,v 代表一条边 接下来 q 行 Q 代表询问,接下来 k 个数代表组织的人数,读入为 0时停止读入。 C 代表修改,输入 x,w 代表将 x 的分值变为 w

Output

共 Q 的数量行,为开荒的人的总分值

Sample Input

4 4

10 5 2 2

1 2

2 3

2 4

Q 3 4 0

C 3 200

Q 3 4 0

Q 1 4 0

Sample Output

9

207

17

样例解释:

第一次询问,参加的人有 2,3,4 5+2+2=9

第一次修改,权值为 10 5 200 2

第二次询问,参加的人有 2,3,4 5+200+2=207

第三次询问,参加的人有 1,2,4 10+5+2=17

Data Constraint

数据范围:

20%的数据 n<=10000,q<=500;

另外 20%的数据 k=2

另外 20%的数据 没有修改操作

所有数据 n,q<=100000,所有询问 k 的和<=1000000

保证数据合法

这道题朴素算法的话O(qk^2logn)以上的复杂度,每次询问暴力跑k^2遍LCA,记录加和得到答案。

正解需要建立虚树,我的理解就是在原树种中取出重要的点:那k个询问点,和他们的LCA

这些重要的点的点权之和就是我们答案所求。

用了dfs序(dfn)进行优化,很简单的一个思想:记录每个点自己和子树中所有节点能到达的最大dfn记为last

每次LCA时根据dfn排序后i与i+1进行找LCA就可以包括所有重要的点了,虽然会有重复,去重即可。

由于我们要计算点权,可以发现这些重要的点连成的所有链上的点权即为所求。于是可以按dfn序依次进栈,若当前栈顶last还没有新进来的那个点dfn大,说明这个点肯定不在前面那颗子树中,于是top--,直到top == 0或者找到这个点所在的子树。

若top == 0,加上这个点权就好;

若找到子树,用树上差分(当然也可以树链剖分)维护找到距离就好。

随后用ans统计答案,就可以在接近O(qklogn)(可能还快一点)完成。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt(x) putchar(x)
#define ex pt('\n')
const int MAXN = 1e6 + 5;
int n,q;
int dfn[MAXN],last[MAXN],Stack[MAXN<<1];
ll v[MAXN],dep[MAXN],dis[MAXN],f[MAXN][20];
ll sum[MAXN]; 
int head[MAXN<<1],cnt = 0;
ll ans = 0;
bool flag = 0;
int ask[MAXN<<2],node[MAXN];
struct edge
{
	int next,to;
}e[MAXN<<1];
void add(int u,int v)
{
	e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
	e[++cnt].next = head[v]; e[cnt].to = u; head[v] = cnt;
}
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

void dfs(int now,int fr)
{
	dep[now] = dep[fr] + 1;
	dfn[now] = ++cnt;
	f[now][0] = fr;
	for(int i = 1;i <= 19;i++)
		f[now][i] = f[f[now][i-1]][i-1];
	for(int i = head[now];i;i = e[i].next)
	{
		int to = e[i].to;
		if(to == fr) continue;
		dfs(to,now);	
	}
	last[now] = cnt;
}

void updata(int x,ll v) {for(x;x <= n;x += x&-x) sum[x] += v;}

ll Ask(int x)
{
	ll res = 0;
	for(x;x;x -= x&-x) res += sum[x];
	return res;
}

inline void init()
{
	in(n); in(q);
	for(int i = 1;i <= n;i++) lin(v[i]);
	for(int i = 1;i < n;i++) {int u,v; in(u); in(v); add(u,v);}
	cnt = 0; dfs(1,0);
	for(int i = 1;i <= n;i++) 
		updata(dfn[i],v[i]),updata(last[i]+1,-v[i]);
}

int Lca(int x,int y)
{
	if(dep[x] < dep[y]) swap(x,y);
	for(int i = 19;i >= 0;i--)
		if(dep[f[x][i]] >= dep[y])
			x = f[x][i];
	if(x == y) return x;
	for(int i = 19;i >= 0;i--)
		if(f[x][i] != f[y][i])
			x = f[x][i],y = f[y][i];
	return f[x][0];
}

bool cmp(int x,int y)
{
	return dfn[x] < dfn[y];
}

inline void work()
{
	while(q--)
	{
		char ch = getchar();
		if(ch == 'Q')
		{
			ask[0] = 0;
			while(1)
			{
				in(ask[++ask[0]]);
				if(!ask[ask[0]]) {ask[0]--;break;}	
			} 
			sort(ask+1,ask+1+ask[0],cmp);
			int tmp = ask[0];
			for(int i = 1;i < tmp;i++)
				ask[++ask[0]] = Lca(ask[i],ask[i+1]);
			sort(ask+1,ask+1+ask[0],cmp);
			ask[0] = unique(ask+1,ask+1+ask[0]) - ask - 1;
			ans = 0; int top = 0;
			for(int i = 1;i <= ask[0];i++)
			{
				while(top && last[Stack[top]] < dfn[ask[i]]) top--;
				if(top) ans += Ask(dfn[ask[i]]) - Ask(dfn[Stack[top]]);
				else ans += v[ask[i]];
				Stack[++top] = ask[i];
			}
			out(ans); ex;
		}
		else
		{
			int x; ll w;
			in(x); lin(w);
			swap(w,v[x]);
			w = v[x] - w;
			updata(dfn[x],w);
			updata(last[x]+1,-w);
		}
	}
}

int main()
{
	init();
	work();
	return 0;
}
/*
4 4
10 5 2 2
1 2
2 3
2 4
Q 3 4 0
C 3 200
Q 3 4 0
Q 1 4 0
*/

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83153329