HGOI-国庆七连测-day3

题解

讲道理,真的很谴责这种挂着羊头卖着恐龙肉的操作。标题写着普及训练难度结果是NOI/CTSC的题orz
真的爆零,我一道题都不会写orz,出题人是江苏高考415的dalao…


第一题——高考题(gaokao)

【题目描述】

  • 给出二元组序列 [ a i , b i ] [a_i,b_i]
  • 给出方程: T i = { a i + b i i = 1 m a x ( T i 1 , j = 1 i a i ) + b i i 1 T_i=\begin{cases} a_i+b_i &i=1\\ max(T_{i-1},\sum_{j=1}^{i}{a_i})+b_i &i\ne 1\\ \end{cases}
  • 现在要求重新对二元组序列排序,使得序列T当中的最大的值最小。

  • 这个真的是高考题…orz,那我高考的时候怕是要gg了…

  • 其实你可以手动推一下:

    T 1 = a 1 + b 1 T_1=a_1+b_1

    T 2 = m a x ( T 1 , a 1 + a 2 ) + b 2 = m a x ( a 1 + b 1 + b 2 , a 1 + a 2 + b 2 ) T_2=max(T_1,a_1+a_2)+b_2=max(a_1+b_1+b_2,a_1+a_2+b_2)

    T 3 = m a x ( T 2 , a 1 + a 2 + a 3 ) + b 3 = m a x ( a 1 + b 1 + b 2 + b 3 , a 1 + a 2 + b 2 + b 3 , a 1 + b 1 + b 2 + b 3 ) T_3=max(T_2,a_1+a_2+a_3)+b_3=max(a_1+b_1+b_2+b_3,a_1+a_2+b_2+b_3,a_1+b_1+b_2+b_3)

  • 总结规律你会发现其实 T i = m a x ( k = 1 j a i + k = j i b i ) T_i=max(\sum_{k=1}^{j}a_i+\sum_{k=j}^{i}b_i)

  • 然后你会发现这个并没有什么用…

  • 只能告诉你按照 m i n ( a i , b j ) < m i n ( a j , b i ) min(a_i,b_j)<min(a_j,b_i) 来排…

  • 但这个还是错解…但数据是错误解造出来的orz

  • 相似题目(其实是一样的)可以看这个洛谷2123,lzj大佬写的是正解…


#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
inline void fff(){
	freopen("gaokao.in","r",stdin);
	freopen("gaokao.out","w",stdout);
}
const int N=100010;
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
int n;
struct node{
	LL t1,t2;
	LL d;
	bool operator < (const node x)const{
//		if(d!=x.d) return d<x.d;
//		if(d<=0) return t1<x.t1;
//		return t2>x.t2;
		return min(t1,x.t2)<min(t2,x.t1);
	}
}a[N];
LL c[N];
int main(){
//	fff();
	int T;
	cin>>T;
	while(T--){
		memset(a,0,sizeof(a));
		memset(c,0,sizeof(c));
		n=read();
		for(int i=1;i<=n;i++){
			a[i].t1=(LL)read();
			a[i].t2=(LL)read();
			if(a[i].t1>a[i].t2) a[i].d=1;
			else if(a[i].t1<a[i].t2) a[i].d=-1;
			else a[i].d=0;
		}
		sort(a+1,a+n+1);
		LL sum=0;
		for(int i=1;i<=n;i++){
			sum+=a[i].t1;
			c[i]=max(c[i-1],sum)+a[i].t2;
		}
		printf("%lld\n",c[n]);
	}

}

第二题——高数题(gaoshu)

【题目描述】

  • 在点权树中,边上存在颜色,规定所走路径不能前后经过相邻的颜色,求树上合法路径权值和。

  • 唯一一个还能够想想做法的题。和上一道和下一道的玄之又玄的做法完全不一样。
  • 三十分做法:暴力枚举开始节点 i i ,然后对于每一个节点 O ( n ) O(n) 模拟,最终结果由于出发节点和终点是等价的,所以结果除以2。最终复杂度 O ( n 2 ) O(n^2)
  • 满分做法:
    • 假设根节点root,则从根节点出发向下进行路径操作。对于每一颗子树来说,他子树下的节点的权值经过和是固定的。那么维护下从dang下面节点延伸到当前节点的权值和与he合法途径的条数。
    • 显然:
      • 当前节点为根节点的所有合法路径的权值和 = 之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的分数+
        之前深搜的所有子节点向上返回的分数之和 * 当前子节点返回的边数+之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的边数 * 当前点的权。
    • 由于要考虑颜色,则需要将当前子树的相同路径的子树进行“合并操作”。
    • 然后就可以求出解了orz。

#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map> 
using namespace std;
typedef long long LL;
const int N=300010;
inline void fff(){
	freopen("gaoshu.in","r",stdin);
	freopen("gaoshu.out","w",stdout);
}
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
int n;
int a[N],d[N];
struct Edge{
	int from,to,color;
};
vector<Edge> edge;
vector<int> G[N];
map<int,pair<LL,LL>> tt[N];
bool visited[N];
bool cmp1(int i,int j){
	return edge[i].color<edge[j].color;
}
LL num[N],val[N];
LL ans;
void dfs(int u,int fuck){
	int siz=G[u].size();
	visited[u]=true;
	sort(G[u].begin(),G[u].end(),cmp1);
	for(int i=0;i<siz;i++){
		Edge &e=edge[G[u][i]];
		if(visited[e.to]) continue;
		dfs(e.to,e.color);
		ans+=(num[u]-tt[u][e.color].first)*val[e.to]+(val[u]-tt[u][e.color].second)*num[e.to]+(num[u]-tt[u][e.color].first)*num[e.to]*a[u];
		tt[u][e.color].first+=num[e.to];
		tt[u][e.color].second+=val[e.to];
		num[u]+=num[e.to];
		val[u]+=val[e.to];
		ans+=val[e.to]+num[e.to]*a[u];
	}
	num[u]-=tt[u][fuck].first;
	val[u]-=tt[u][fuck].second;
	num[u]++;
	val[u]+=a[u]*num[u];
}
int main(){
//	fff();
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++){
		int u,v,w;
		u=read(),v=read(),w=read();
		edge.push_back((Edge){u,v,w});
		G[u].push_back(edge.size()-1);
		edge.push_back((Edge){v,u,w});
		G[v].push_back(edge.size()-1);
	}
	ans=0;
	dfs(1,-1);
	cout<<ans;
}

第三题——高中题(gaozhong)

【题目描述】

  • 给出n个节点,m条带权边,前n-1条是好边,要求调整边的权值使得图的最小生成树只由好边构成。求调整权值代价和最小是多少(是指每对边权修改1代价就是1)

  • 很明显,这个题我不会。
  • 可以看得出,如果要让好边都是最小生成树当中的边,根据kruskal算法,好边的权值只能够减,坏边的权值只能够加。那么令任意一组好、坏边 i , j i,j ,修改前的权值是 w i , w j w_i,w_j ,调整值是 d i , d j d_i,d_j ,那么可以得出 w i d i w j + d j w_i-d_i\leq w_j+d_j ,再往后推一步,就可以得到 w i w j d i + d j w_i-w_j\leq d_i+d_j ,而我们的目的就是求出满足这些不等式组的非负最小解(最小是指和最小)。
  • 那我们就可以将他们转化成二分图的模型,好边属于同一个集合,坏边属于一个集合,要求好边与坏边之间的匹配值满足不等式。
  • 然后就是KM求最大完美匹配了orz。

#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=550;
inline void fff(){
	freopen("gaozhong.in","r",stdin);
	freopen("gaozhong.out","w",stdout);
}
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
const int INF=10000000;
int n,m,nx,ny;
int s[N],e[N],d[N],belong[N],ss[N];
int f[N][N],z[N][N],lx[N],ly[N];
bool visx[N],visy[N];
bool dfs(int u,int pre,int id){
	if(u==e[id]) return true;
	for(int i=1;i<=n;i++){
		if(i!=pre&&f[u][i]&&dfs(i,u,id)){
			z[f[u][i]][id]=d[f[u][i]]-d[id];
			return true;
		}
	}
	return false;
}
bool dfs(int x){
	visx[x]=true;
	for(int i=1;i<=ny;i++){
		if(!visy[i]){
			int relax=lx[x]+ly[i]-z[x][i];
			if(!relax){
				visy[i]=true;
				if(!belong[i]||dfs(belong[i])){
					belong[i]=x;
					return true;
				}
			}else ss[i]=min(ss[i],relax);
		}
	}
	return false;
}
void KM(){
	for(int i=1;i<=nx;i++) lx[i]=-INF;
	for(int i=1;i<=nx;i++)
		for(int j=1;j<=ny;j++)
			lx[i]=max(lx[i],z[i][j]);
	for(int i=1;i<=nx;i++){
		memset(ss,0x3f,sizeof(ss));
		while(true){
			memset(visx,false,sizeof(visx));
			memset(visy,false,sizeof(visy));
			if(dfs(i)) break;
			int delta=INF;
			for(int j=1;j<=ny;j++)
				if(!visy[j]) delta=min(delta,ss[j]);
			if(delta==INF) return;
			for(int j=1;j<=nx;j++)
				if(visx[j]) lx[j]-=delta;
			for(int j=1;j<=ny;j++)
				if(visy[j]) ly[j]+=delta;
					else ss[j]-=delta;
		}
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		s[i]=read();
		e[i]=read();
		d[i]=read();
	}
	memset(f,0,sizeof(f));
	memset(z,0,sizeof(z));
	for(int i=1;i<n;i++) f[s[i]][e[i]]=f[e[i]][s[i]]=i;
	for(int i=n;i<=m;i++) dfs(s[i],0,i);
	nx=ny=m;
	KM();
	int ans=0;
	for(int i=1;i<n;i++)ans+=lx[i];
	for(int i=n;i<=m;i++)ans+=ly[i];
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/82932542
今日推荐