轻功
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
*/