海亮 7.5 模拟赛

海亮7.5模拟赛

#A. 道路负载

将每一条边的边权设置为它连接的两个点的权值最小值 然后我们从小到大加边并统计两个集合之间的权值

按照从大到小加边策略的原因:

你先弄出一条大边 那么无论如何这两个点之间的\(c''\)就是这条大边 且在这两个点所代表的所有集合的所有点都可以通过这一条边连通

如果左面或者右面集合有大边的话 左右一定是通过这条"较小边"连通的 而且左面右面集合有小边的情况是不存在的 所以保证了正确性

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long 
const int N = 4e5 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , ans , a[N];

int fa[N] , sz[N];
int find ( int x )
{
	if ( fa[x] == x ) return x;
	return fa[x] = find ( fa[x] );
}

struct nn { int u , v , val; } e[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int u = read() , v = read();
		e[i].u = u , e[i].v = v;
		e[i].val = min ( a[u] , a[v] );
	}
	sort ( e + 1 , e + m + 1 , [](const nn a , const nn b) { return a.val > b.val; } );
	for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i , sz[i] = 1;
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int fu = find ( e[i].u ) , fv = find ( e[i].v );
		if ( fu == fv ) continue;
		ans += sz[fu] * sz[fv] * e[i].val;
		fa[fu] = fv;
		sz[fv] += sz[fu];		
	}
	cout << ans << endl;
	return 0;
}

#B. 小根堆

如果没有题目中的条件限制 那么我们设置\(f[x]\)为以这个点为根的小根堆的方案个数

那么\(f[x]=C_{sz[x]-1}^{sz[ls]}*f[ls]*f[rs]\)

可以\(dfs\)提前维护数组\(sz\)\(f[i]\) 也可以记忆化写

对于询问\(x,y\) 对它有影响的只有\(x\)到根节点的\(logn\)个点

\(g[x][y]\)为第\(x\)个节点且这个点值为\(y\)的方案数

那么初始值\(g[x][y]= dp(x) * C ( n - y , sz[x] - 1 )\)

考虑到只有\(x\)到根上的所有节点会被考虑到 那么我们可以将\(x\)一维滚掉

先将前一个\(x\)记录在\(temp\)中 再将\(x>>1\) 那么柿子就是:

\(g[i] = sum[i+1] \% mod * dp(temp^1) \% mod * C ( n - i - sz[temp] , sz[temp^1] );\)

非常优美的结论:方案数=排列数*选值数

记录一下调试的错误:

  1. 后缀和求成了前缀和
  2. 阶乘从1开始了
  3. 未知错误:只有记忆化写\(f[i]\)才能过 否则无法通过
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ls (x<<1)
#define rs (x<<1|1)
#define int long long 
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , fac[N] , inv[N] , sz[N] , f[N] , g[N] , x , y , sum[N];

void init ()
{
	fac[0] = inv[0] = fac[1] = inv[1] = 1;
	for ( int i = 2 ; i <= n ; i ++ )
		inv[i] = ( ( ( mod - mod / i ) * inv[mod%i] ) % mod + mod ) % mod , fac[i] = fac[i-1] * i % mod;
	for ( int i = 2 ; i <= n ; i ++ ) // 求阶乘逆元
		inv[i] = inv[i-1] * inv[i] % mod;
}

int C ( int a , int b )//a中b个 
{
	if ( b > a ) return 0;
	return fac[a] % mod * inv[b] % mod * inv[a-b] % mod;
}

void dfs ( int x )
{
	sz[x] = 1;
	if ( ls <= n ) dfs ( ls ) , sz[x] += sz[ls];
	if ( rs <= n ) dfs ( rs ) , sz[x] += sz[rs];
}

int dp ( int x )
{
	if ( f[x] != -1 ) return f[x];
	if ( sz[x] < 3 ) return 1;//只有一种情况
	int ans = dp(ls) * dp(rs) % mod * C ( sz[x] - 1 , sz[ls] ) % mod;
	return f[x] = ans; 
}


void solve()
{
	g[y] = dp(x) * C ( n - y , sz[x] - 1 ) % mod;
	while ( x != 1 )
	{
		int temp = x; x >>= 1;
		for ( int i = n ; i ; i -- ) 
			sum[i] = ( sum[i+1] + g[i] ) % mod;
		for ( int i = 1 ; i <= n ; i ++ )
			g[i] = sum[i+1] % mod * dp(temp^1) % mod * C ( n - i - sz[temp] , sz[temp^1] );
	}
	cout << g[1] << endl;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	memset ( f , -1 , sizeof f );
	n = read() , x = read() , y = read();
	init();
	dfs(1);
	solve();
	return 0;
}

#C. 模式匹配

我们考虑贪心 对于四元组肯定是越早取越好

所以我们记录每一个值的前一个值\(pre[i]\)(见\(hh\)的项链)

那么对于一个二元组的第二个值 那么查询左面的端点有没有值 如果有就计入答案同时向右面移动区间

否则继续覆盖区间

注意特判四个点都一样的情况(具体实现为设置\(chk\)数组) \(last\)必须初始化为\(1\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long 
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , a[N] , lst[N] , pre[N] , chk[N] , t[N] , ans;

int lowbit ( int x ) { return x & (-x); }

void upd ( int x , int val )
{
	for ( int i = x ; i <= n ; i += lowbit(i) ) t[i] += val;
}

int query ( int x )
{
	int res = 0;
	for	( int i = x ; i ; i -= lowbit(i) )
		res += t[i];
	return res;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read();
		pre[i] = lst[a[i]];
		lst[a[i]] = i;
	}
	int lstt = 1;
	for ( int i = 1 ; i <= n ; i ++ )
	{
		++chk[a[i]];
		if ( chk[a[i]] == 4 ) 
		{
			for ( int j = lstt ; j <= i ; j ++ ) --chk[a[j]];
			ans += 4 , lstt = i + 1; continue;
		}
		if ( pre[i] < lstt ) continue;
		if ( query(pre[i]) > 0 )
		{
			for ( int j = lstt ; j <= i ; j ++ ) --chk[a[j]];
			ans += 4 , lstt = i + 1;
		}
		upd ( pre[i] + 1 , 1 ) , upd ( i , -1 );
	}
	cout << ans << endl;		
	return 0;
}
posted @ 2023-07-07 16:25  Echo_Long  阅读(16)  评论(0编辑  收藏  举报