对于一类题目,它们建出来的图好大好大,这时候通常可以利用线段树(大多数时候是可持久化线段树)来进行优化连边,保证点的连通性不变,路径权值信息不变。
前言:这道题当时是我在Noip吧问的,年代比较久远。
给出三个数列{Xi},{Ai},{Bi}。
若存在一个长度为m数列{Pi},满足P递增且P[1]=1,P[m]=n。
那么该数列P的权值sum按以下方式计算:
sum=0
for i = 2 to m
if(X[P[i]]<=X[P[i-1]])
then sum = sum + A[P[i-1]]
else sum = sum + B[P[i-1]]
问在所有满足条件的数列中,权值第k小的序列的权值。
n<=20001,k<=1000000。
显然,如果k为1的话,我们可以列出DP式。
设f[i,j]表示这个P数列的第j个是i时最小权值。
f[i,j]=mink<i(f[k,j−1]+(x[i]<=x[k])?a[k]:b[k])
那么现在,对于k小,要怎么解决呢?总不可能设多一维来表示第几小吧?
我们注意到,可以对于任意 i<j 且x[i]<=x[j],i连j权值为a[i]的有向边,若x[i]>x[j]就连权值为b[i]的有向边。
那么现在只需要跑1到n的K短路就好了。
可是,数据大小不容许我们暴力建出图来,边的数量会达到n^2!
这是,我们可以考虑使用可持久化线段树优化连边。
设Ti表示为1~i的x数组组成的线段树(可以看做一个桶用线段树维护),那么i的入边来自Ti-1,而我们把前i-1个点分成了log n组,所以只有log n条边。然后相邻两颗线段树之间的对应区间(不同的那log n个)再连条边。我们就成功用可持久化线段树优化了建图,边的数量只有n log n。
前言:这是philipsweng大神出的题目。
恶梦是学校里面的学生会主席。他今天非常的兴奋,因为学校一年一度的学生节开始啦!!
在这次节日上总共有N个节目,并且总共也有N个舞台供大家表演。其中第i个节目的表演时间为第i个单位时间,表演的舞台为Ai,注意可能有多个节目使用同一个舞台。
作为恶梦的忠实粉丝之一的肥佬,当然要来逛一下啦,顺便看一下能不能要到恶梦的签名。
肥佬一开始会先在A1 看完节目1再去闲逛。
肥佬可以在舞台之间随便乱走。但是假如肥佬当前在看第i个节目,站在第Ai个舞台前面的话,由于有些道路被封锁了,所以肥佬下一步只能前往第Li到第Ri个舞台中的一个。并且当一个节目结束的时候,肥佬只能去看另外一个节目,或者结束自己的闲逛。
具体而言就是说,假设肥佬可以从第i个节目走去第j个节目,那么当且仅当 i<j 且Li <= Aj <= Ri。
但事实上是,恶梦非常讨厌被自己的粉丝跟踪。所以他想在只封锁掉一个节目的情况下,使得肥佬不能到达自己所在的地方。并且为了防止意外,他还想知道有多少个这样的节目。
简而言之,恶梦想知道对于任意一个节目p∈[1,N],有多少个节目t,使得删掉t之后,不存在一条从节目1出发到节目p的路径。注意,节目1和节目p也是可以被删的。
由于他非常的忙碌,所以他把这个任务交给了你。
N <= 50000。
我们考虑暴力连边,建出图以后,我们可以用dominator tree来做(这是一个DAG,所以不需要用到lengauer tarjan算法,可以直接倍增建树)。
#include<cstdio>
#include<algorithm>
#include<deque>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=5000+10;
deque<int> dl;
int d[maxn],f[maxn][15],r[maxn],a[maxn],h[maxn],go[1000000],next[1000000],ans[maxn];
//int from[maxn*maxn];
int i,j,k,l,t,n,m,now,tot;
void add(int x,int y){
go[++tot]=y;
//from[tot]=x;
next[tot]=h[x];
h[x]=tot;
r[y]++;
}
int lca(int x,int y){
if (d[x]<d[y]) swap(x,y);
int j;
if (d[x]!=d[y]){
j=floor(log(n)/log(2));
while (j>=0){
if (d[f[x][j]]>=d[y]) x=f[x][j];
j--;
}
}
if (x==y) return x;
j=floor(log(n)/log(2));
while (j>=0){
if (f[x][j]!=f[y][j]){
x=f[x][j];
y=f[y][j];
}
j--;
}
return f[x][0];
}
int main(){
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,1,n){
scanf("%d%d",&k,&l);
fo(j,i+1,n)
if (k<=a[j]&&a[j]<=l) add(i,j);
}
//fo(i,1,tot) printf("%d %d\n",from[i],go[i]);
fo(i,2,n)
if (!r[i]){
t=h[i];
while (t){
r[go[t]]--;
t=next[t];
}
}
dl.push_back(1);
d[1]=1;
while (!dl.empty()){
now=dl.front();
dl.pop_front();
d[now]=d[f[now][0]]+1;
fo(i,1,floor(log(n)/log(2))) f[now][i]=f[f[now][i-1]][i-1];
t=h[now];
while (t){
j=go[t];
if (!f[j][0]) f[j][0]=now;else f[j][0]=lca(f[j][0],now);
r[j]--;
if (!r[j]) dl.push_back(j);
t=next[t];
}
}
fo(i,1,n){
ans[i]=(d[i])?d[i]:-1;
printf("%d\n",ans[i]);
}
}
可是暴力建图显然不行。
可以参考例题1,那么我们可以使用可持久化线段树优化连边,再用dominator tree跑就好了。
前言:第一次在别的地方做比赛(contest hunter),遇上了这题。当时打网络流结果只得到了爆搜分(水10分虐酸神)。
Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中。
但是她从未停止过和恋人 Velding 的书信往来。一天,她准备去探访他。
对着窗外的阳光,临行前她再次弹起了琴。
她的琴的发声十分特殊。
让我们给一个形式化的定义吧。
所有的 n 个音符形成一棵由音符 C ( 1 号节点) 构成的有根树,每一个音符有一个音高 H_i 。
Arietta 有 m 个力度,第 i 个力度能弹出 D_i 节点的子树中,音高在 [L_i,R_i] 中的任意一个音符。
为了乐曲的和谐,Arietta 最多会弹奏第 i 个力度 T_i 次。
Arietta 想知道她最多能弹出多少个音符。
1 ≤ n, m ≤ 10000 。
1 ≤ H_i , T_i , P_i ≤ n, 1 ≤ L_i ≤ R_i ≤ n 。
显然可以网络流建图。
然后咧,我们容易想到弄出dfs序,然后用线段树套线段树/平衡树来优化连边。
题解说,因为是维护子树内的元素,所以用线段树合并来优化建图,注意要对线段树进行可持久化(出题人dwj后来说这是可以被卡的,因为线段树合并在叶节点时会加一层,所以如果所有点的点权都相同就会有N层然后就。。。)。