POI 2014 切题记

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 n106

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 n5000

Solution:

  • 看到数据范围,考虑 Θ ( n 2 ) \Theta(n^2) Θ(n2)的做法,也比较明显。
  • 首先考虑以 x x x为子树的方案。
  • 小数据模拟不难发现兄弟之间的方案为 C c n t − 1 3 C_{cnt-1}^{3} Ccnt13,以及对 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,n106

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 Ain 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} 2rili+1。若有,输出此数,否则,输出 0 0 0
n , m ≤ 500000 n ,m\le500000 n,m500000

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 n200000,m106

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 n106,m100,Li109

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,Coli106

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 n500000,ti109

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 − &gt; y x-&gt;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 n106,1S109,ti109

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值