2019.07.10【NOIP提高組】模擬 A 組 比賽總結

題目

樹的難題

Time Limits: 2000 ms
Memory Limits: 262144 KB

Description

在這裏插入圖片描述

Input

輸入文件爲split.insplit.in
第一行 包含 一個正整數 T,表示有T組測試數據 。接下來 依次是 T組測試數 據。
每組測試數 據的第一行包含個正整數N。
第二行包含 N個 0、1、2之一 的整數,依次 表示點 1到點 N的顏色。其中0表示黑色, 1表示白色, 2表示灰色。
接下來 N-1行 ,每行爲三個整數 ui、vi、ci,表示 一條權值等於 ci的邊(ui,vi)(ui, vi)

Output

輸出文件爲split.outsplit.out
輸出 T行 ,每一個整數, 依次 表示 每組測試數據 的答案。

Sample Input

1
5
0 1 1 1 0
1 2 5
1 3 3
5 2 5
2 4 16

Sample Output

10
【樣例解釋】
花費 10 的代價刪去 邊(1, 2)和邊(2, 5)。

Data Constraint

對於 10% 的數據: 1 ≤ N ≤ 10。
對於 30% 的數據: 1 ≤ N ≤ 50 0。
對於 60% 的數據: 1 ≤ N ≤ 50 000 。
對於 100% 的數據: 1 ≤ N ≤ 300 000 ,1 ≤ T ≤ 5,0 ≤ ci ≤ 10^9。


祕密任務

Time Limits: 1000 ms
Memory Limits: 262144 KB

Description

在這裏插入圖片描述

Input

輸入文件爲secret.insecret.in
第一行 包含一 個正整數 T,表示有 T組測試數據。接下來 依次是 T組測試數 據。
每組測試數 據的第一行包含兩個整N、M。
第二行包含 N - 1個正整數,依次表示 A1,A2, …,AN-1。
接下來 M行,每 行爲三個 整數: ui、vi、ci,表示一條連接城市ui和城市 vi的路程等於 ci的高速公路。

Output

輸出文件爲secret.outsecret.out
輸出 T行, 依次 表示 每組測試數據 的答案 。若最優 方案 唯一 則輸出 ”Yes”和 最小 代價 ,否則 輸出 ”No ”和最小 代價 。字符串 和整數 之間 請用一個 空格 隔開 。

Sample Input

3
3 3
2 4
1 3 23
3 2 12
2 1 11
4 4
3 2 2
1 2 1
2 3 1
3 4 1
4 1 1
3 4
3 2
1 2 1
2 3 2
2 3 19
3 1 4

Sample Output

Yes 4
Yes 3
No 2
【樣例解釋】
第 1組測試數據: 最優 方案 是在城市 1設立 兩個 檢查點 。
第 2組測試數據: 最優 方案 是城市 1的高速公路 (1, 4 )的出入口設立 檢查點。
第 3組測試數據: 最優 方案 是在城市 2設立 一個 檢查點 ,不過 既可以 設置 在 高速公路 (1, 2)的出入 口,也可以 設置 在高速公路 (2, 3)的出入口 。

Data Constraint

對於 10% 的數據: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。
另有 40% 的數據: 最優 方案 是唯一 的。
對於 10 0% 的數據: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。無向圖可能有重邊。


查詢

Time Limits: 3000 ms
Memory Limits: 524288 KB

Description

對於一個整數序列,查詢區間第k大數可以在O(logN)的時間內輕鬆完成。現在我們對這個問題進行推廣。

考慮帶重複數的集合(multiset)。定義在該類集合上的並操作“+”爲兩個集合的所有數不剔除重複得到的結果。比如,若A={1,2,2,3},B={2,3,4,4},則C={1,2,2,2,3,3,4,4}。

對於一個給定序列A[1…N],定義A[x…y]爲包含y-x+1個元素的集合{A[x],A[x+1],…,A[y]}。現在,給定整數序列A,你需要回答很多詢問,其中第i個詢問要求集合A[x[i,1]…y[i,1]]+A[x[i,2]…y[i,2]]+…+A[x[i,ki]…y[i,ki]]中第pi小的元素。

Input

輸入的第一行包含兩個整數N和M,分別表示整數序列的長度和詢問的個數。
第二行N個整數給出整數序列A。
第3到第M+2行給出了所有的詢問。第i+2行前兩個整數爲ki和pi,接下來2ki個整數給出x[i, 1], y[i, 1], x[i,2], …, x[i, ki], y[i, ki]。這些數按照題目中的定義描述了第i個詢問。

Output

對於每一個詢問,輸出相應的結果,即從小到大排序後的第pi個元素。

Sample Input

8 3
1 2 3 1 2 3 1 2
2 4 1 4 3 7
2 4 1 3 6 8
5 31 1 8 1 8 1 8 1 8 1 8

Sample Output

1
2
3

Data Constraint

第1、2個測試數據滿足N ≤ 1 000,M ≤ 1 000。

第3、4個測試數據滿足ki = 1。

所有的測試點滿足N ≤ 200 000,M ≤ 200 000,0 ≤ A[i] ≤ 1 000 000,ki ≤ 5。
保證1x[i,j]N,1y[i,j]N,1pij(y[i,j]x[i,j]+1)1 \leq x[i,j] \leq N, 1 \leq y[i, j] \leq N, 1 \leq pi \leq \sum_j(y[i, j] - x[i, j] + 1)

Hint

【樣例說明】
第一個詢問集合爲1,2,3,1,3,1,2,3,1{1,2,3,1,3,1,2,3,1},第4小元素爲1。


總結

今天的題目難度適中,數據終於不水了。
得分:0+0+100=100
讀了一遍題,感覺T3最水,裸的主席樹,不切都不好意思了,於是開始剛。。。
先把原數列離散化,建樹時動態開點(詳見線段樹詳解),其中第 i 棵權值線段樹儲存[1..i][1..i]中每一個數出現的次數。詢問時把第y棵線段樹減去第x1x-1棵線段樹,再查詢。
10點左右,程序打完了,交了上去,不料跑了2min+都沒有跑完。
完美卡OJヾ(⌐■_■)ノ♕!
後來發現,可以一邊減一邊查詢,因爲我們只需要一條從根到底的路徑,而相減會得出許多無用的路徑。
然後就AC了。
接着打了1個多小時的T2暴力,卻連SPFA都沒有調出來,完美爆〇(後來發現是因爲我建邊時有下標爲0的邊,而遍歷一個點連接的邊時把這條邊判掉了)


正解

T1

不妨把無根樹轉化成以1爲根的樹。
FiF_i表示 i 所在的子樹含有0個黑色節點的最優解,GiG_i表示 i 所在的子樹含有0個白點的最優解,HiH_i表示 i 所在的子樹含有1個白色節點的最優解。

  1. 那麼當 i 爲黑色點時
    Fk=Gk=isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i))Hk=minjsonkHj+isonk,ijmin(Fi+cost(k,i),Gi,Hi+cost(k,i)) \begin{aligned} F_k&=\infty\\ G_k&=\sum_{i\subseteq son_k}\min(F_i+cost(k,i),G_i,H_i+cost(k,i))\\ H_k&=\min_{j\subseteq son_k} H_j+\sum_{i\subseteq son_k,i\neq j}\min(F_i+cost(k,i),G_i,H_i+cost(k,i)) \end{aligned}
  2. 當 i 爲白色點時
    Fk=isonkmin(Fi,Gi+cost(k,i),Hi+cost(k,i))Gk=Hk=isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i)) \begin{aligned} F_k&=\sum_{i\subseteq son_k}\min(F_i,G_i+cost(k,i),H_i+cost(k,i))\\ G_k&=\infty\\ H_k&=\sum_{i\subseteq son_k}\min(F_i+cost(k,i),G_i,H_i+cost(k,i)) \end{aligned}
  3. 當 i 爲灰色點時
    Fk=isonkmin(Fi,Gi+cost(k,i),Hi+cost(k,i))Gk=isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i))Hk=minjsonkHj+isonk,ijmin(Fi+cost(k,i),Gi,Hi+cost(k,i)) \begin{aligned} F_k&=\sum_{i\subseteq son_k}\min(F_i,G_i+cost(k,i),H_i+cost(k,i))\\ G_k&=\sum_{i\subseteq son_k}\min(F_i+cost(k,i),G_i,H_i+cost(k,i))\\ H_k&=\min_{j\subseteq son_k} H_j+\sum_{i\subseteq son_k,i\neq j}\min(F_i+cost(k,i),G_i,H_i+cost(k,i)) \end{aligned}

然後從底到根DP。
這樣子做似乎就可以了,但是
在這裏插入圖片描述
題目中有一句話:

對於 100% 的數據: 1 ≤ N ≤ 300 000

於是@#%#¥%@%@#*)¥~#¥@……
其實解決方法也很容易:BFS一遍,然後把隊列反過來就可以了。

T2

首先可以發現不在最短路上的點都是沒有用的。
於是只保留那些可以更新某個點的最短路的邊。
然後答案要求用最小的費用使1~n不連通,這就是最小割,即最大流
於是答案就出來了,現在的關鍵就是判斷它是否唯一。
有一個不顯然的性質:

有向邊(u,v)(u,v)必定出現在任意最小割當且僅當殘量網絡中存在S到u的路徑和v到T的路徑(此處的路徑指的是由沒有滿流的弧組成的路徑)。

那麼如果出現在任意最小割中的邊的原始容量之和不等於答案,方案就不唯一。
這裏可以從1和n分別做一次DFS,最後判斷一下就可以了。
特殊地,當一條邊的au=ava_u=a_v時,答案不唯一。


CODE

T1

#include<cstdio>
using namespace std;
#define ll long long
#define N 300005
#define M 1000005
#define inf 300000000000000
int fir[N],end[N<<1],nex[N<<1],a[N],data[N],fa[N];
ll f[N],g[N],h[N],cost[N<<1],temp;char ch;
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll min(ll x,ll y,ll z){return x<z&&x<y?x:min(y,z);}
inline char gc()
{
	static char buf[M],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x)
{
	while(ch=gc(),ch<'0'||ch>'9');x=ch-'0';
	while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-'0';
}
int main()
{
	int t,n,k,i,l,x,y,z,m,head,tail;
	read(t);
	while(t--)
	{
		read(n),m=0;
		for(i=1;i<=n;i++) read(a[i]),fir[i]=0;
		for(i=1;i<n;i++)
		{
			read(x),read(y),read(z);
			end[++m]=y,cost[m]=z,nex[m]=fir[x],fir[x]=m;
			end[++m]=x,cost[m]=z,nex[m]=fir[y],fir[y]=m;
		}
		head=0,tail=1,data[1]=1;
		while(head<tail)
		{
			k=data[++head];
			for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
			{
				fa[end[i]]=k;
				data[++tail]=end[i];
			}
		}
		for(l=n;l;l--)
		{
			k=data[l];
			if(!a[k])//black
			{
				f[k]=h[k]=inf,g[k]=0;
				for(i=fir[k];i;i=nex[i])
					if(end[i]!=fa[k])
						g[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
				for(i=fir[k];i;i=nex[i])
					if(end[i]!=fa[k])
						h[k]=min(h[k],h[end[i]]+g[k]-min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]));
			}
			else if(a[k]<2)//white
			{
				f[k]=h[k]=0,g[k]=inf;
				for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
				{
					f[k]+=min(f[end[i]],g[end[i]]+cost[i],h[end[i]]+cost[i]);
					h[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
				}
			}
			else//gray
			{
				h[k]=inf,f[k]=g[k]=0;
				for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
				{
					f[k]+=min(f[end[i]],g[end[i]]+cost[i],h[end[i]]+cost[i]);
					g[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
				}
				for(i=fir[k];i;i=nex[i])
					if(end[i]!=fa[k])
						h[k]=min(h[k],h[end[i]]+g[k]-min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]));
			}
		}
		printf("%lld\n",min(f[1],g[1],h[1]));
	}
	return 0;
}

T2

#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define inf 400000000000
#define M 8005
#define N 405
struct EDGE{ll end,lenth,next;}edge[M];
ll n,m,s,sum,a[N],fir[N],lenth[M],end[M],nex[M],dis[N],GAP[N],data[200000];
bool exist[N],b[N],bz[N];
inline ll min(ll x,ll y){return x<y?x:y;}
inline void inc(ll x,ll y,ll z)
{
	end[++s]=y,lenth[s]=z,nex[s]=fir[x],fir[x]=s;
	end[++s]=x,lenth[s]=z,nex[s]=fir[y],fir[y]=s;
}
inline void add(ll x,ll y,ll z)
{
	edge[++s]=(EDGE){y,z,fir[x]},fir[x]=s;
	edge[++s]=(EDGE){x,0,fir[y]},fir[y]=s;
}
inline void spfa()
{
	int i,head=0,tail=1,u,v;
	for(i=2;i<=n;i++) dis[i]=inf,exist[i]=1;
	data[1]=1,dis[1]=0;
	while(head<tail)
	{
		u=data[++head],exist[u]=1;
		for(i=fir[u];i;i=nex[i])
		{
			v=end[i];
			if(dis[v]>dis[u]+lenth[i])
			{
				dis[v]=dis[u]+lenth[i];
				if(exist[v]) exist[v]=0,data[++tail]=v;
			}
		}
	}
}
ll sap(ll k,ll flow)
{
	if(k==n) return flow;
	ll i,j,t,have=flow;
	for(i=fir[k];i;i=edge[i].next)
		if(edge[i].lenth&&dis[k]==dis[j=edge[i].end]+1)
		{
			t=sap(edge[i].end,min(edge[i].lenth,have));
			have-=t,edge[i].lenth-=t,edge[i^1].lenth+=t;
			if(!have) return flow;
		}
	if(!(--GAP[dis[k]])) GAP[1]=n;
	++GAP[++dis[k]];
	return flow-have;
}
void dfs1(ll k)
{
	b[k]=0;
	for(ll i=fir[k];i;i=edge[i].next)
		if(edge[i].lenth&&b[edge[i].end])
			dfs1(edge[i].end);
}
void dfs2(ll k)
{
	bz[k]=0;
	for(ll i=fir[k];i;i=edge[i].next)
		if(edge[i^1].lenth&&bz[edge[i].end])
			dfs2(edge[i].end);
}
int main()
{
	ll task,i,j,x,y,z,ans=0;
	scanf("%lld",&task);
	while(task--)
	{
		scanf("%lld%lld",&n,&m);
		for(i=1;i<n;i++) scanf("%lld",a+i),fir[i]=0;
		a[n]=inf,fir[n]=0,s=1;
		for(i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld",&x,&y,&z);
			inc(x,y,z);
		}
		spfa();
		memset(fir,0,sizeof(fir));
		for(i=s,s=1;i>1;i--)
			if(dis[end[i]]+lenth[i]==dis[end[i^1]])
				add(end[i],end[i^1],min(a[end[i]],a[end[i^1]]));
		memset(dis,0,sizeof(dis));
		memset(GAP,0,sizeof(GAP)),ans=0;
		while(dis[1]<n) ans+=sap(1,inf);
		memset(b,1,sizeof(b));
		memset(bz,1,sizeof(bz));
		dfs1(1),dfs2(n);
		for(sum=0,i=2;i<=s;i+=2)
			if((!b[edge[i^1].end])&&(!bz[edge[i].end]))
			{
				if(a[edge[i^1].end]==a[edge[i].end])
					{sum=0;break;}
				sum+=min(a[edge[i^1].end],a[edge[i].end]);
			}
		if(sum==ans) printf("Yes %lld\n",ans);
		else printf("No %lld\n",ans);
	}
	return 0;
}

T3

#include<cstdio>
#include<algorithm>
using namespace std;
#define M 7200005
#define N 200005
struct number
{
	int num,id;
}rec[N];
struct node
{
	int sum,lson,rson;
	node(){sum=lson=rson=0;}
}a[M];
int cnt,num[N],b[N],x[10],y[10];char ch;
inline char gc()
{
	static char buf[M],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x)
{
	while(ch=gc(),ch<'0'||ch>'9');x=ch-'0';
	while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-'0';
}
bool cmp(number x,number y){return x.num<y.num;}
void add(int x,int y,int l,int r,int num)
{
	a[x].sum=a[y].sum+1;
	if(l==r) return;
	int mid=l+r>>1;
	if(num<=mid)
	{
		a[x].rson=a[y].rson,a[x].lson=++cnt;
		add(a[x].lson,a[y].lson,l,mid,num);
	}
	else
	{
		a[x].lson=a[y].lson,a[x].rson=++cnt;
		add(a[x].rson,a[y].rson,mid+1,r,num);
	}
}
int query(int k,int l,int r,int num)
{
	if(l==r) return l;
	int mid=l+r>>1,i,s=0;
	for(i=1;i<=k;i++) s+=a[a[y[i]].lson].sum-a[a[x[i]].lson].sum;
	if(s>=num)
	{
		for(i=1;i<=k;i++) x[i]=a[x[i]].lson,y[i]=a[y[i]].lson;
		query(k,l,mid,num);
	}
	else
	{
		for(i=1;i<=k;i++) x[i]=a[x[i]].rson,y[i]=a[y[i]].rson;
		query(k,mid+1,r,num-s);
	}
}
int main()
{
	int n,m,i,j,k,t;
	read(n),read(m),cnt=n+1;
	for(i=1;i<=n;i++) read(rec[i].num),rec[i].id=i;
	sort(rec+1,rec+n+1,cmp),rec[0].num=-1;
	for(i=1,j=0;i<=n;i++)
	{
		if(rec[i].num>rec[i-1].num) b[++j]=rec[i].num;
		num[rec[i].id]=j;
	}
	for(i=1;i<=n;i++) add(i,i-1,1,n,num[i]);
	while(m--)
	{
		read(j),read(k);
		for(i=1;i<=j;i++)
		{
			read(x[i]),read(y[i]),x[i]--;
			if(x[i]>=y[i]) i--,j--;
		}
		printf("%d\n",b[query(j,1,n,k)]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章