2019.07.10【NOIP提高组】模拟 A 组 比赛总结

题目

树的难题

Time Limits: 2000 ms
Memory Limits: 262144 KB

Description

在这里插入图片描述

Input

输入文件为 s p l i t . i n split.in split.in
第一行 包含 一个正整数 T,表示有T组测试数据 。接下来 依次是 T组测试数 据。
每组测试数 据的第一行包含个正整数N。
第二行包含 N个 0、1、2之一 的整数,依次 表示点 1到点 N的颜色。其中0表示黑色, 1表示白色, 2表示灰色。
接下来 N-1行 ,每行为三个整数 ui、vi、ci,表示 一条权值等于 ci的边 ( u i , v i ) (ui, vi) (ui,vi)

Output

输出文件为 s p l i t . o u t split.out split.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

输入文件为 s e c r e t . i n secret.in secret.in
第一行 包含一 个正整数 T,表示有 T组测试数据。接下来 依次是 T组测试数 据。
每组测试数 据的第一行包含两个整N、M。
第二行包含 N - 1个正整数,依次表示 A1,A2, …,AN-1。
接下来 M行,每 行为三个 整数: ui、vi、ci,表示一条连接城市ui和城市 vi的路程等于 ci的高速公路。

Output

输出文件为 s e c r e t . o u t secret.out secret.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。
保证 1 ≤ x [ i , j ] ≤ N , 1 ≤ y [ i , j ] ≤ N , 1 ≤ p i ≤ ∑ j ( 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) 1x[i,j]N,1y[i,j]N,1pij(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} 1,2,3,1,3,1,2,3,1,第4小元素为1。


总结

今天的题目难度适中,数据终于不水了。
得分:0+0+100=100
读了一遍题,感觉T3最水,裸的主席树,不切都不好意思了,于是开始刚。。。
先把原数列离散化,建树时动态开点(详见线段树详解),其中第 i 棵权值线段树储存 [ 1.. i ] [1..i] [1..i]中每一个数出现的次数。询问时把第y棵线段树减去第 x − 1 x-1 x1棵线段树,再查询。
10点左右,程序打完了,交了上去,不料跑了2min+都没有跑完。
完美卡OJヾ(⌐■_■)ノ♕!
后来发现,可以一边减一边查询,因为我们只需要一条从根到底的路径,而相减会得出许多无用的路径。
然后就AC了。
接着打了1个多小时的T2暴力,却连SPFA都没有调出来,完美爆〇(后来发现是因为我建边时有下标为0的边,而遍历一个点连接的边时把这条边判掉了)


正解

T1

不妨把无根树转化成以1为根的树。
F i F_i Fi表示 i 所在的子树含有0个黑色节点的最优解, G i G_i Gi表示 i 所在的子树含有0个白点的最优解, H i H_i Hi表示 i 所在的子树含有1个白色节点的最优解。

  1. 那么当 i 为黑色点时
    F k = ∞ G k = ∑ i ⊆ s o n k min ⁡ ( F i + c o s t ( k , i ) , G i , H i + c o s t ( k , i ) ) H k = min ⁡ j ⊆ s o n k H j + ∑ i ⊆ s o n k , i ≠ j min ⁡ ( F i + c o s t ( k , i ) , G i , H i + c o s t ( 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} FkGkHk==isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i))=jsonkminHj+isonk,i̸=jmin(Fi+cost(k,i),Gi,Hi+cost(k,i))
  2. 当 i 为白色点时
    F k = ∑ i ⊆ s o n k min ⁡ ( F i , G i + c o s t ( k , i ) , H i + c o s t ( k , i ) ) G k = ∞ H k = ∑ i ⊆ s o n k min ⁡ ( F i + c o s t ( k , i ) , G i , H i + c o s t ( 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} FkGkHk=isonkmin(Fi,Gi+cost(k,i),Hi+cost(k,i))==isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i))
  3. 当 i 为灰色点时
    F k = ∑ i ⊆ s o n k min ⁡ ( F i , G i + c o s t ( k , i ) , H i + c o s t ( k , i ) ) G k = ∑ i ⊆ s o n k min ⁡ ( F i + c o s t ( k , i ) , G i , H i + c o s t ( k , i ) ) H k = min ⁡ j ⊆ s o n k H j + ∑ i ⊆ s o n k , i ≠ j min ⁡ ( F i + c o s t ( k , i ) , G i , H i + c o s t ( 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} FkGkHk=isonkmin(Fi,Gi+cost(k,i),Hi+cost(k,i))=isonkmin(Fi+cost(k,i),Gi,Hi+cost(k,i))=jsonkminHj+isonk,i̸=jmin(Fi+cost(k,i),Gi,Hi+cost(k,i))

然后从底到根DP。
这样子做似乎就可以了,但是
在这里插入图片描述
题目中有一句话:

对于 100% 的数据: 1 ≤ N ≤ 300 000

于是@#%#¥%@%@#*)¥~#¥@……
其实解决方法也很容易:BFS一遍,然后把队列反过来就可以了。

T2

首先可以发现不在最短路上的点都是没有用的。
于是只保留那些可以更新某个点的最短路的边。
然后答案要求用最小的费用使1~n不连通,这就是最小割,即最大流
于是答案就出来了,现在的关键就是判断它是否唯一。
有一个不显然的性质:

有向边 ( u , v ) (u,v) (u,v)必定出现在任意最小割当且仅当残量网络中存在S到u的路径和v到T的路径(此处的路径指的是由没有满流的弧组成的路径)。

那么如果出现在任意最小割中的边的原始容量之和不等于答案,方案就不唯一。
这里可以从1和n分别做一次DFS,最后判断一下就可以了。
特殊地,当一条边的 a u = a v a_u=a_v au=av时,答案不唯一。


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;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/95384077
今日推荐