POI 2014 Salad Bar
Description:
有一个长度为
n
n
n的字符串,每一位只会是
p
p
p或
j
j
j。你需要取出一个子串
S
S
S(从左到右或从右到左一个一个取出),使得不管是从左往右还是从右往左取,都保证每时每刻已取出的
p
p
p的个数不小于j的个数。你需要最大化
∣
S
∣
|S|
∣S∣。
n
≤
1
0
6
n \le 10^6
n≤106
Solution:
- 先从左到右扫一遍得到以每个点为左端点,只考虑从左往右取,最远能取到哪,并标记每一个点最早被哪一个点扫到。
- 然后再从右往左扫一遍,开两个栈:
- 一个记录当前仍符合条件的右端点;
- 一个记录已经被扫过的左端点。
- 每当有元素从第一个栈里弹出时,就在第二个栈里二分找出符合两个条件的最远的左端点。
Code:
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize("Ofast")
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 1000002
int n;
char s[N];
int cnt[N];
int l[N],r[N];
int stk1[N<<1],q;
int stk2[N<<1],p;
int ans;
int find(int L,int R){
while(L<R){
int mid=(L+R)>>1;
if(r[stk2[mid]]>=stk1[q]) L=mid+1;
else R=mid;
}
return L;
}
int main(){
scanf("%d%s",&n,s+1);
REP(i,1,n) cnt[i]=cnt[i-1]+(s[i]=='p'?1:-1);
REP(i,1,n){
while(q && cnt[i]-cnt[stk1[q]-1]<0) r[stk1[q--]]=i-1;
if(q) l[i]=stk1[1];
else if(cnt[i]-cnt[i-1]==1) l[i]=i;
else l[i]=INF;
if(cnt[i]-cnt[i-1]==1) stk1[++q]=i;
}
while(q) r[stk1[q--]]=n;
DREP(i,n,1){
while(q && cnt[stk1[q]]-cnt[i-1]<0) chkmax(ans,stk1[q]-max(l[stk1[q]],stk2[find(2,p+1)-1])+1),--q;
if(cnt[i]-cnt[i-1]==1) {
stk1[++q]=i;
while(p && r[stk2[p]]<=r[i]) p--;
stk2[++p]=i;
}
}
while(q) chkmax(ans,stk1[q]-max(l[stk1[q]],stk2[find(2,p+1)-1])+1),--q;
printf("%d",ans);
}
POI 2014 Hotel
Description:
有一棵
n
n
n个结点的树,求三点两两距离相等的点集数。
n
≤
5000
n \le 5000
n≤5000
Solution:
- 看到数据范围,考虑 Θ ( n 2 ) \Theta(n^2) Θ(n2)的做法,也比较明显。
- 首先考虑以 x x x为子树的方案。
- 小数据模拟不难发现兄弟之间的方案为 C c n t − 1 3 C_{cnt-1}^{3} Ccnt−13,以及对 x x x以上的点的贡献为 C c n t [ d e p i ] 2 C_{cnt[dep_i]}^{2} Ccnt[depi]2。
- 这样好像遍历所有以 x x x为子树,稍微进行 d p dp dp即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 5002
int n;
int qwq,head[N];
struct edge{
int to,nxt;
}E[N<<1];
void addedge(int x,int y){E[qwq]=(edge){y,head[x]};head[x]=qwq++;}
#define LREP(x) for(int i=head[x];~i;i=E[i].nxt)
int cnt[N],Mx;
ll dp[N][2];
ll ans;
void dfs(int x,int f,int dep){
++cnt[dep];
chkmax(Mx,dep);
LREP(x){
int y=E[i].to;
if(y==f)continue;
dfs(y,x,dep+1);
}
}
int main(){
qwq=0;
mcl(head,-1);
Rd(n);
SREP(i,1,n){
int a,b;
Rd(a),Rd(b);
addedge(a,b);
addedge(b,a);
}
ll ans=0;
REP(x,1,n){
mcl(dp,0);
LREP(x){
mcl(cnt,0);Mx=0;
int y=E[i].to;
dfs(y,x,1);
REP(j,1,Mx){
ans+=dp[j][1]*cnt[j];
dp[j][1]+=dp[j][0]*cnt[j];
dp[j][0]+=cnt[j];
}
}
}
printf("%lld\n",ans);
return 0;
}
POI 2014 Bricks
Description:
有
n
n
n种颜色的砖块,每种颜色的砖块有
a
i
a_i
ai块。要将它们排成一排,使其满足相邻两块颜色不同,且起点砖块的颜色为
s
s
s终点砖块的颜色为
t
t
t。求合法的构造方案。
m
=
∑
a
i
,
n
≤
1
0
6
m=\sum{a_i},n\le10^6
m=∑ai,n≤106
Solution:
- 看数据范围以及题意是构造题。
- 直接考虑贪心构造,先放颜色剩余数目少的,当然还要满足与上一个放的颜色不同,那么堆维护即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 1000002
int n,m,s,t;
struct node{
int col,cnt;
bool operator<(const node &_)const{
if(cnt==_.cnt)return col!=t;
return cnt<_.cnt;
}
};
priority_queue<node>Q;
int ans[N];
int main(){
Rd(n),Rd(s),Rd(t);
if(n==1){
int x;Rd(x);
if(x!=1)puts("0");
else puts("1");
return 0;
}
REP(i,1,n){
int cnt;Rd(cnt);
if(i==s)--cnt;
if(i==t)--cnt;
if(cnt<0){puts("0");return 0;}
if(cnt)Q.push((node){i,cnt});
}
ans[++m]=s;
while(!Q.empty()){
node now=Q.top();Q.pop();
if(now.col==ans[m]){
if(Q.empty()){puts("0");return 0;}
node tmp=Q.top();
Q.pop();Q.push(now);
ans[++m]=tmp.col;
tmp.cnt--;
if(tmp.cnt)Q.push(tmp);
}
else {
ans[++m]=now.col;
now.cnt--;
if(now.cnt)Q.push(now);
}
}
if(ans[m]==t)puts("0");
else {
ans[++m]=t;
REP(i,1,m) printf("%d ",ans[i]);
puts("");
}
return 0;
}
POI 2014 Couriers
Description:
有一个长度为
n
n
n的序列
A
{A}
A,满足
A
i
≤
n
A_i\le n
Ai≤n。
m
m
m个询问,问区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]中是否存在一个数出现次数大于
r
i
−
l
i
+
1
2
\frac{r_i-l_i+1}{2}
2ri−li+1。若有,输出此数,否则,输出
0
0
0。
n
,
m
≤
500000
n ,m\le500000
n,m≤500000
Solution:
- 这应该是一道裸的主席树了。
- 询问一个区间的出现次数大于K的数。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 500002
#define M N*21
int n,m;
int Lson[M],Rson[M];
int root[N],sum[M],tat;
void build(int L,int R,int &p){
p=++tat;
if(L==R)return;
int mid=(L+R)>>1;
build(L,mid,Lson[p]),build(mid+1,R,Rson[p]);
}
void update(int L,int R,int x,int &p,int f){
p=++tat;
Lson[p]=Lson[f];
Rson[p]=Rson[f];
sum[p]=sum[f]+1;
if(L==R)return;
int mid=(L+R)>>1;
if(x<=mid)update(L,mid,x,Lson[p],Lson[f]);
else update(mid+1,R,x,Rson[p],Rson[f]);
}
int query(int L,int R,int l,int r,int k){
if(sum[r]-sum[l]<=k)return 0;
if(L==R)return L;
int mid=(L+R)>>1;
int s1=sum[Lson[r]]-sum[Lson[l]];
int s2=sum[Rson[r]]-sum[Rson[l]];
if(s1>s2)return query(L,mid,Lson[l],Lson[r],k);
else return query(mid+1,R,Rson[l],Rson[r],k);
}
int main(){
Rd(n),Rd(m);
REP(i,1,n){
int x;Rd(x);
update(1,n,x,root[i],root[i-1]);
}
while(m--){
int l,r;
Rd(l),Rd(r);
printf("%d\n",query(1,n,root[l-1],root[r],(r-l+1)/2));
}
return 0;
}
POI 2014 Card
Description:
有
n
n
n张牌排成一排,每张牌上的正反两面都有数。现在,有
m
m
m个询问,询问交换第
a
i
a_i
ai和第
b
i
b_i
bi张牌后,牌的序列能否是单调不降(注意:牌的正反面可以任意翻转)。
n
≤
200000
,
m
≤
1
0
6
n \le200000,m \le10^6
n≤200000,m≤106
Solution:
- 好像很容易就想到线段树上…
- 因为它的操作只是单点修改和区间查询(而且还是固定的)。
- 那么问题就是合并。
- 首先,基于贪心的思想:因为每张牌有 2 2 2个数,那么最优一定要取最小的数。
- 而合并两个区间时,我们只要考虑区间之间的边界的 2 2 2张牌( 4 4 4个数),进行讨论即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize("Ofast")
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 200002
int n,m;
int C[N][2];
struct Tree{
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
#define family tree[p],tree[p<<1],tree[p<<1|1]
struct node{
int L,R;
int can0,can1;
}tree[N<<2];
void Up(node &A,node L,node R){
int t=L.can0;
if(~t){
t=C[L.R][t];
if(t<=C[R.L][0])A.can0=R.can0;
else if(t<=C[R.L][1])A.can0=R.can1;
else A.can0=-1;
}
else A.can0=t;
t=L.can1;
if(~t){
t=C[L.R][t];
if(t<=C[R.L][0])A.can1=R.can0;
else if(t<=C[R.L][1])A.can1=R.can1;
else A.can1=-1;
}
else A.can1=t;
}
void build(int L,int R,int p){
tree[p].L=L,tree[p].R=R;
if(L==R){
tree[p].can0=0,tree[p].can1=1;
return;
}
int mid=(L+R)>>1;
build(lson),build(rson);
Up(family);
}
void update(int p,int x){
if(tree[p].L==tree[p].R)return;
int mid=(tree[p].L+tree[p].R)>>1;
if(x<=mid)update(p<<1,x);
else update(p<<1|1,x);
Up(family);
}
}T;
int main(){
Rd(n);
REP(i,1,n){
Rd(C[i][0]),Rd(C[i][1]);
if(C[i][0]>C[i][1])swap(C[i][0],C[i][1]);
}
T.build(1,n,1);
Rd(m);
while(m--){
int a,b;
Rd(a),Rd(b);
swap(C[a][0],C[b][0]),swap(C[a][1],C[b][1]);
T.update(1,a),T.update(1,b);
puts(~T.tree[1].can0?"TAK":"NIE");
}
return 0;
}
POI 2014 Around the world
Description:
在一个
n
n
n个点的圆上,点与点之间距离为
L
i
L_i
Li。有
m
m
m个询问,求以最大跨度为
d
i
d_i
di,是否能环绕一周,若能,求最小跨的次数,否则,输出
N
I
E
NIE
NIE。
n
≤
1
0
6
,
m
≤
100
,
L
i
≤
1
0
9
n\le 10^6,m\le 100,L_i\le 10^9
n≤106,m≤100,Li≤109
Solution:
- 小数据模拟发现,对于
L
i
L_i
Li,无论以哪一点为起点,还是顺时针逆时针,它的最小跨的次数是不变。
比较明显 - 那么我们就贪心的找到下个点跳到的位置,需要前缀和来维护一下即可。
Code:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 1000002
int n,m;
int dis[N<<1],Mx;
int cnt[N],last[N<<1];
int main(){
Rd(n),Rd(m);
REP(i,1,n)Rd(dis[i]),dis[i+n]=dis[i],chkmax(Mx,dis[i]);
REP(i,1,n<<1)dis[i]+=dis[i-1];
while(m--){
int x;Rd(x);
if(x<Mx){puts("NIE");continue;}
REP(i,1,n)cnt[i]=0,last[i]=i,last[i+n]=i+n;
for(int i=n+1,j=1;i<=(n<<1);++i){
while(dis[i]-dis[j]>x)++j;
cnt[i-n]=(j>n?cnt[j-n]:0)+1;
last[i]=last[j];
if(i-last[i]>=n){
printf("%d\n",cnt[i-n]);
break;
}
}
}
return 0;
}
POI 2014 Criminals
Description:
有一个长度为
n
n
n的颜色序列
C
o
l
Col
Col,现在有两个人取序列中的颜色,一个从左往右取,一个从右往左取,直到两人相遇。现在给你两人取的颜色序列。求两人可能相遇的点。
n
,
C
o
l
i
≤
1
0
6
n,Col_i\le 10^6
n,Coli≤106
Solution:
- 首先对于 一个人的所有方案,需要找到此人的颜色序列的每个颜色的第一次出现的位置,这样一定是最优的。那么就可以预处理出颜色序列位置之间的关系。
- 那么关键就是两人会相遇同一个点,那么基于上述的贪心想法,两人相遇的点为从左往右和从右往左取到最后一个数之间的一个。
- 但是,模拟小数据,发现这个可行方案的区间是非连续的,有多个。
- 那么我们就需要 d p dp dp每一段的左右端点。
- 那么之后统计每一段的贡献即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 1000002
int n,m;
int len1,len2;
int col[N],A[N],B[N];
int nxt1[N],pre1[N],nxt2[N],pre2[N];
int cnt[2];
struct node{
int s,t;
}dp[2][N];
int l[N],r[N];
int sum1[N],sum2[N],res;
int num,ans[N];
void Init1(){
for(int i=pre1[A[1]];i;i=nxt1[i])dp[1][++cnt[1]]=(node){i,i};
int f;
REP(k,2,len1){
f=k&1;
cnt[f]=0;
for(int i=pre1[A[k]],j=1;i;i=nxt1[i]){
while(j<=cnt[f^1] && dp[f^1][j].s<=i)++j;
++cnt[f];
dp[f][cnt[f]].s=i;
dp[f][cnt[f]].t=dp[f^1][j-1].t;
}
}
f=len1&1;
REP(i,1,cnt[f])l[dp[f][i].s]=dp[f][i].t;
}
void Init2(){
for(int i=pre2[B[1]];i;i=nxt2[i])dp[1][++cnt[1]]=(node){i,i};
int f;
REP(k,2,len2){
f=k&1;
cnt[f]=0;
for(int i=pre2[B[k]],j=1;i;i=nxt2[i]){
while(j<=cnt[f^1] && dp[f^1][j].s>=i)++j;
++cnt[f];
dp[f][cnt[f]].s=i;
dp[f][cnt[f]].t=dp[f^1][j-1].t;
}
}
f=len2&1;
REP(i,1,cnt[f])r[dp[f][i].s]=dp[f][i].t;
}
void add(int x){
++sum1[x];
if(sum1[x]==1 && sum2[x]>0)++res;
}
void del(int x){
--sum2[x];
if(sum2[x]==0 && sum1[x]>0)--res;
}
int main(){
Rd(n),Rd(m);
REP(i,1,n)Rd(col[i]);
Rd(len1),Rd(len2);
REP(i,1,len1)Rd(A[i]);
REP(i,1,len2)Rd(B[i]);
DREP(i,n,1){
nxt1[i]=pre1[col[i]];
pre1[col[i]]=i;
}
REP(i,1,n){
nxt2[i]=pre2[col[i]];
pre2[col[i]]=i;
}
Init1();
cnt[1]=0;
Init2();
int now=pre1[B[len2]];
while(!l[now] && now) now=nxt1[now];
if(!now){
puts("0");
return 0;
}
REP(i,r[now]+1,n)++sum2[col[i]];
int L=1,R=r[now];
while(r[now]){
while(L<l[now])add(col[L++]);
while(R<r[now])del(col[R++]);
if(res>=1)ans[++num]=now;
now=nxt1[now];
}
printf("%d\n",num);
REP(i,1,num)printf("%d ",ans[i]);
puts("");
return 0;
}
POI 2014 Farm Craft
** Description:**
有一棵
n
n
n个点的树,从
1
1
1出发到达其它点最后回到
1
1
1(每条边只进过
2
2
2次),而到达一个点会有
t
i
t_i
ti的自动延迟(注意人经过就可以走了),经过一条边的时间为
1
1
1。特别的,点
1
1
1必须在最后返回时自动延迟。求最少花费的总时间。
n
≤
500000
,
t
i
≤
1
0
9
n\le 500000,t_i\le 10^9
n≤500000,ti≤109
Solution:
- 此题模拟就很容易就想到贪心,但是延迟时间是根据访问先后俩决定的,那么这里就必须要 d p dp dp了。
- 分析一下,走完以 x x x为根的子树的时间为 s z [ x ] ∗ 2 + d p [ x ] sz[x]*2+dp[x] sz[x]∗2+dp[x]。
- 那么可以对 x − > y x->y x−>y的 y y y进行贪心排序。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 500002
int n;
int cost[N];
int qwq,head[N];
struct edge{
int to,nxt;
}E[N<<1];
void addedge(int x,int y){E[qwq]=(edge){y,head[x]};head[x]=qwq++;}
int sz[N];
int tmp[N];
int dp[N];
bool cmp(int x,int y){return dp[x]+(sz[y]<<1)>dp[y]+(sz[x]<<1);}
void dfs(int x,int f){
sz[x]=1;
dp[x]=cost[x];
int road=0,num=0;
for(int i=head[x];~i;i=E[i].nxt){
int y=E[i].to;
if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
}
for(int i=head[x];~i;i=E[i].nxt){
int y=E[i].to;
if(y==f)continue;
tmp[++num]=y;
}
sort(tmp+1,tmp+1+num,cmp);
REP(i,1,num){
chkmax(dp[x],dp[tmp[i]]+road+1);
road+=(sz[tmp[i]]<<1);
}
}
int main(){
mcl(head,-1);
Rd(n);
REP(i,1,n)Rd(cost[i]);
SREP(i,1,n){
int a,b;
Rd(a),Rd(b);
addedge(a,b);
addedge(b,a);
}
dfs(1,0);
printf("%d\n",max(dp[1],cost[1]+((n-1)<<1)));
return 0;
}
POI 2014 Freight
Description:
有
n
n
n辆火车,要往返两站,而站与站之间花费的时间为
S
S
S,特别的,每辆车初始都在同一站,每辆车都有一个最早发车时间
t
i
t_i
ti。且必须要满足每辆正在行驶的车是同方向的,每辆车在某一站的发车时间必须相差
1
1
1。求所有车往返两站花费的最短时间。
n
≤
1
0
6
,
1
≤
S
≤
1
0
9
,
t
i
≤
1
0
9
n \le 10^6,1\le S \le 10^9, t_i \le 10^9
n≤106,1≤S≤109,ti≤109
Solution:
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define db double
#define ll long long
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
#define N 1000002
int n;
ll S;
ll t[N];
ll dp[N];
struct BIT{
#define lowbit(x) (x&-x)
ll bit[N];
void init(){
mcl(bit,INF);
}
void add(ll x,ll v){
while(x<=n){
chkmin(bit[x],v);
x+=lowbit(x);
}
}
ll query(ll x){
ll res=inf;
while(x){
chkmin(res,bit[x]);
x-=lowbit(x);
}
return res;
}
}T;
struct p1{
int find(int L,int R,int x){
while(L<R){
int mid=(L+R)>>1;
if(dp[mid]-mid-1<=t[x]-x)L=mid+1;
else R=mid;
}
return L;
}
void solve(){
T.init();
REP(i,1,n){
int pos=find(1,i,i);
dp[i]=t[i]-(pos-1)+S+i-1;
if(pos!=i)chkmin(dp[i],T.query(n-pos+1)+S+i*2);
T.add(n-i+1,dp[i]-(i+1)*2);
}
printf("%lld\n",dp[n]);
}
}p1;
struct p2{
void solve(){
mcl(dp,INF);
int j=0;
dp[j]=0;
REP(i,1,n) {
while(j<i && dp[j]+i-j-1<t[i])++j;
dp[i]=t[i]-j+S+i;
if(j!=i)chkmin(dp[i],dp[j]+S+(i-j-1)*2);
}
printf("%lld\n",dp[n]);
}
}p2;
int main(){
Rd(n),Rd(S);S<<=1;
REP(i,1,n){
Rd(t[i]);
if(i>1)chkmax(t[i],t[i-1]+1);
}
srand(time(NULL));
int op=rand()%10;
if(!op)p1.solve();//O(nlogn)
else p2.solve();//O(n);
return 0;
}