2020 Multi-University Training Contest 2补题

学艺不精,菜鸡落泪。

A. HDOJ-6763 Total Eclipse

题意

给你一个 n n n个点, m m m条边的无向图,第 i i i个点有一个权值 b i b_i bi,你每次要选择可操作的最大的一个连通子图,并将子图上所有点的权值减 1 1 1,求出最小操作次数。

思路

先看正向思路:很明显是选择每一个连通块内最小的点权,并将整个连通块内所有的点都减去这个点权。将操作后 b i = 0 b_i=0 bi=0的点删掉,并对新的连通块重复此操作,记录操作次数即为答案。
但是删点操作过于麻烦,这里要将过程反向,因为点权越大越留到后面,所以按点权降序排序,依次将点加入进图中并建图,用并查集维护连通块,每个连通块内权值最小的点为这个连通块的根。
当加入 x x x时,遍历与 x x x节点相邻的点,若为已经加入进来的点且不与 x x x联通,设这个点所在的连通块的根节点 y y y,则将 x x x y y y联通的贡献为 b y − b x b_y-b_x bybx。这里我们思考一下此处操作的正向含义是什么——我们删掉了 x x x点并且将 x x x所在的连通块断裂成了一个或多个连通块,并且这些点的 b i b_i bi全部减去 b x b_x bx,贡献 b y − b x b_y-b_x bybx就可以理解为:删掉 x x x后,分裂出来的 y y y所在连通块内下一个节点被删的最小操作次数。
完成上述过程后,统计每个最大连通块的根节点的 b b b值,并计入贡献。

举个例子

2020 Multi-University Training Contest 2补题_第1张图片

代码

//#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
int fa[maxn];
int findfa(int x)
{
	if(fa[x]!=x)
		fa[x]=findfa(fa[x]);
	return fa[x];
}
int val[maxn];
vector<int>G[maxn];
bool vis[maxn];
signed main()
{
	std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int t,n,m,u,v;
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		vector<int>rec;
		for(int i=1;i<=n;i++)
		{
			cin>>val[i];
			G[i].clear();
			vis[i]=0;
			fa[i]=i;
			rec.push_back(i);
		}
		for(int i=1;i<=m;i++)
		{
			cin>>u>>v;
			G[u].push_back(v);
			G[v].push_back(u);
		}
		sort(rec.begin(),rec.end(),[](const int &a,const int &b){
				return val[a]>val[b];
			});
		ll ans=0;
		for(int &u:rec)
		{
			vis[u]=1;//按点权顺序将u加入
			for(int &v:G[u])
			{
				if(!vis[v])
					continue;
				int y=findfa(v);//v节点在之前已加入,找到v节点的根
				if(u==y)
					continue;
				ans+=val[y]-val[u];
				fa[y]=u;//将新加进来的作为连通块根结点,方便得到最小权值
			}
		}
		for(int i=1;i<=n;i++)
			if(fa[i]==i)//最后将每个连通块根节点加上
				ans+=val[i];
		cout<<ans<<endl;
	}
	return 0;
}

J. HDOJ-6772 New Equipments

没什么好说的,读完就知道是爆搜,卡常过于恶心。用vector不管怎么剪枝都是TLE。

代码

//#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
//#define DEBUG//#define lowbit(x) ((x) & -(x))//<
void read(){}
template<typename T,typename... T2>inline void read(T &x,T2 &... oth) {
	x=0; int ch=getchar(),f=0;
	while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	if(f)x=-x;
	read(oth...);
}
ll ans=0;
struct node
{
	int w[5];
	node(){}
	node(int a,int b,int c,int d)
	{
		w[1]=a;
		w[2]=b;
		w[3]=c;
		w[4]=d;
	}
} arr[55][55];
int n,k,cnt[55],nex[55];
void dfs(int x,ll a,ll b,ll c,ll d)
{
	if(x>k)
	{
		ans=max(ans,a*b*c*d);
		return;
	}
	for(int i=1;i<=cnt[x];i++)
		dfs(nex[x],a+arr[x][i].w[1],b+arr[x][i].w[2],c+arr[x][i].w[3],d+arr[x][i].w[4]);
	return;
}
signed main()
{
	std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int T,t,a,b,c,d;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&k);
		for(int i=1;i<=k;i++)
			cnt[i]=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&t);
			cnt[t]++;
			for(int j=1;j<=4;j++)
				scanf("%d",&arr[t][cnt[t]].w[j]);
		}
		int pre=k+1;
		for(int i=k;i>=1;i--)
		{
			if(cnt[i])
			{
				nex[i]=pre;
				pre=i;
			}
		}
		ans=0;
		dfs(pre,100,100,100,100);
		printf("%lld\n",ans);
	}
	return 0;
}

你可能感兴趣的:(OJ上的做题经验,HDOJ)