海亮 7.8 周赛

\(100+100+50+45+1=296pts\)

被薄纱 乐 歇斯底里地打完部分分 发现别人已经打完了正解 我不好说()

#A. 摧毁遗迹

做法显然 排序后取最大值和次大值轮流攻击即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;

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

int n , k , cnt , st , a[N];

signed main ()
{
	n = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	sort ( a + 1 , a + n + 1 , [](const int a , const int b) { return a > b; } );
	int tmp[2] = { a[1] , a[2] };
	while ( k > 0 )
	{
		k -= tmp[st];
		st ^= 1;
		cnt ++;	
	}
	printf ( "%d" , cnt );
	return 0;
}

#B. 区间游戏

观察样例可得做法:

按照区间大小从小到大排序 那么从最小的区间开始打标记 这个区间里没有被标记的数就是答案 再向上覆盖区间 因为小区间一定在大区间之前被覆盖 所以正确性是可以保证的

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;

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

int n , k , vis[N];

struct node { int l , r , len , id , ans; } a[N];

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].len = a[i].r - a[i].l + 1 , a[i].id = i;
	sort ( a + 1 , a + n + 1 , [](const node &a , const node &b) { return a.len < b.len; } );
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = a[i].l ; j <= a[i].r ; j ++ )
			if ( !vis[j] ) a[i].ans = j , vis[j] = 1;
	sort ( a + 1 , a + n + 1 , [](const node &a , const node &b) { return a.id < b.id; } );
	for ( int i = 1 ; i <= n ; i ++ )
		printf ( "%d %d %d\n" , a[i].l , a[i].r , a[i].ans );
	return 0;
}

#C. 前方之风

考场上想了一个假二分 打完后发现样例2没有单调性 寄

正解是离线+双指针移动 我们发现\(k\)具有单调性 即\(k\)大的询问 覆盖区间一定比\(k\)小的询问大

#include <bits/stdc++.h>
using namespace std;
#define mid ((l+r)/2)
#define int long long
const int N = 1e6 + 5;

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

int n , q , a[N] , sum[N];

struct query { int val , id , res; } k[N];

void solve()
{
	for ( int i = 1 , l = 1 ; l <= n ; l ++ )
	{
		while ( sum[l] - k[i].val * ( n - l + 1 ) <= a[l] * ( n - l + 1 ) && i <= q ) k[i].res = n - l + 1 , i ++;
	}
}

signed main ()
{
	int T = read();
	while ( T -- )
	{
		memset ( sum , 0 , sizeof sum );
		memset ( k , 0 , sizeof k );
		n = read() , q = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 ; i <= q ; i ++ ) k[i].val = read() , k[i].id = i;
		sort ( a + 1 , a + n + 1 );
		sort ( k + 1 , k + q + 1 , [](const query &a , const query &b) { return a.val > b.val; } );
		for ( int i = n ; i ; i -- ) sum[i] = sum[i+1] + a[i];
		solve();
		sort ( k + 1 , k + q + 1 , [](const query &a , const query &b) { return a.id < b.id; } );
		for ( int i = 1 ; i <= q ; i ++ ) printf ( "%lld " , k[i].res );
		puts("");
	}
	return 0;
}

P8971 『GROI-R1』 虹色的彼岸花

对于一个全部都是有权值边的树 你定了根的权值 那么这棵子树的所有点的取值都确定了

那么对于无权边 就将图分成了森林 我们对于每一个森林处理即可(通过打标记dfs实现)

最终达到的目标就是求出一个满足全部点的范围 然后将所有森林的取值范围都相乘即可

那么我们设一个解为\(x+k\)的形式 设置\(k=val[x]\) 那么对于一个子节点\(y\) 连边为\(e[i].w\) 可以得到\(l\le x + val[x] \le r\)

那么我们\(y=e[i].w-(x+val[x])=e[i].w-x-val[x]\)

那么此时更新系数\(val[y]=e[i].w-val[x]\) 带入可得\(y=-x+val[y]\) 那么\(x=val[y]-y\)

我们用这个东西来更新\(x\)的取值范围 \(y\)的取值范围就是\(l,r\) 那么我们得到此时\(val[y]-r\le x\le val[y]-l\) 进而可以更新最小的\(x\)取值范围上下界

这是\(x\)前系数为正的情况 那么对于这个\(y\) 前面系数就要和父亲相反 也是类似的情况 此时将上柿中的\(x\)改为\(-x\) 即可得出\(l-val[y]\le x\le r-val[y]\)

所以我们开两个数组 \(val\)\(typ\) 维护的是该点后面的系数和该点前面的系数\((0/1)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
const int mod = 1e9 + 7;

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

int n , a[N] , vis[N], nowl , nowr , typ[N] , val[N] , l , r , ans;

int head[N] , cnt;
struct node { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w } , head[u] = cnt; }

void dfs ( int u )
{
	vis[u] = 1;
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( vis[v] ) continue;
		typ[v] = typ[u] ^ 1;
		val[v] = e[i].w - val[u];
		if ( typ[u] ) nowl = max ( nowl , val[v] - r ) , nowr = min ( nowr , val[v] - l );	
		else nowl = max ( nowl , l - val[v] ) , nowr = min ( nowr , r - val[v] );
		dfs(v);
	}
}

signed main ()
{
	int T = read();
	while ( T -- )
	{
		ans = 1 , cnt = 0;
		memset ( head , 0 , sizeof head );
		memset ( vis , 0 , sizeof vis );
		memset ( val , 0 , sizeof val );
		memset ( typ , 0 , sizeof typ );
		n = read() , l = read() , r = read();
		for ( int i = 1 ; i < n ; i ++ )
		{
			int op = read() , u = read() , v = read() , w;
			if ( op ) w = read() , add ( u , v , w ) , add ( v , u , w );
		}
		for ( int i = 1 ; i <= n ; i ++ )
		{
			if ( vis[i] ) continue;
			nowl = l , nowr = r;
			typ[i] = 1;
			dfs(i);
			if ( nowl > nowr ) ans = 0;
			else ans = ans * ( nowr - nowl + 1 ) % mod;						
		}
		printf ( "%lld\n" , ans );
	}
	return 0;
}

P6748 『MdOI R3』Fallen Lord

\(deg[x]\)表示\(x\)点的度数 那么题目中就是要让每个点的连边中最少有\(\lfloor \frac {deg[x]} {2}\rfloor+1\) 条边小于等于\(a[x]\)

也就是最多有\(\lceil\frac {deg[x]}{2} \rceil-1\)条边大于等于\(a[x]\)

我们设置\(dp[x][0/1]\)表示点\(x\)的答案

其中\(dp[x][0]\)表示\(x\)连向父亲的边权值小于等于\(a[x]\)时的答案

\(dp[x][1]\)表示这个权值大于\(a[x]\)\(x\)子树内最大的边权和

考虑转移

\(dp[x][0]\)

考虑\(x\)的每个儿子\(y\)\(p\)\(x->y\)的边权小于等于\(a[x]\)的时候 \(y\)子树内的边权和+\(x->y\)的边权和 \(q\)为大于\(a[x]\)的情况

\(p=max(dp[y][0]+min(a[x],a[y]),dp[y][1]+a[x])\)

解释:如果\(y\)连向父亲的权值小于等于\(a[y]\) 那么取满足条件的\(a[x]\)\(a[y]\)最小值即可

否则边权只能为\(a[x]\)

\(q=max(dp[y][0]+a[y],dp[y][1]+m)\)

解释:如果\(y\)连向父亲的权值小于等于\(a[y]\) 那么取符合条件的\(a[y]\)即可(还是要满足小于等于\(a[y]\)的条件)

否则直接给上界权值\(m\)

那么我们求出来所有的\(q\)\(p\) 不难发现 \(dp[i][0]\)的情况可以取得\(now[i]\)个 否则对于\(dp[i][1]\)的情况可以取得\(now[i]-1\)

对每一个\(q-p\)排序 \(f[i]\)为前缀和 那么对于\(dp[i][0]\)的最终答案就是子树的所有\(p\) 加上\(f[now[i]]\)

树形\(dp\)即可

一个细节:如果我们的\(now[i]<=1\) 那么不可能通过儿子产生贡献 记为\(-inf\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define int long long 
const int N = 5e5 + 5;
const int inf = 9e18;

inline 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 , a[N] , b[N] , dag[N] , sum[N] , f[N][2] , num[N];//我们开一个t[i]数组实时维护它和当前len的差 

int head[N] , cnt;
struct node { int to , nxt; } e[N<<2];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }

void dfs ( int u , int fa )
{
	int ss = 0 , tot = 0;
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == fa ) continue;
		dfs ( v , u );
	}
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == fa ) continue; 
		int p = max ( f[v][0] + min ( a[u] , a[v] ) , f[v][1] + a[u] );
		int q = max ( f[v][0] + a[v] , f[v][1] + m );
		ss += p;
		b[++tot] = q - p;
	}
	sort ( b + 1 , b + 1 + tot , greater<int>() );
	for ( int i = 1 ; i <= tot ; i ++ ) sum[i] = sum[i-1] + b[i];
	f[u][0] = ss + sum[num[u]] , f[u][1] = ( num[u] >= 1 ) ? ss + sum[num[u]-1]: -inf;
}

char ch;
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 < n ; i ++ )
	{
		int u = read() , v = read();
		add ( u , v ) , add ( v , u );
		dag[u] ++ , dag[v] ++;
	}
	for ( int i = 1 ; i <= n ; i ++ ) num[i] = ( dag[i] + 1 ) / 2 - 1;
	dfs ( 1 , 0 );
	cout << max ( f[1][0] , f[1][1] ) << endl;
	return 0;
}


posted @ 2023-07-08 16:29  Echo_Long  阅读(15)  评论(1编辑  收藏  举报