2022 CSP-S 补题

P8817 [CSP-S 2022] 假期计划

最暴力的想法是 预处理最短路并枚举四个点 考虑优化

可以枚举 \(b\)\(c\) 来降低复杂度

对于每一个点 处理和它距离小于等于 \(k+1\) 而且能在 \(k+1\) 步内到达的点的前 \(3\) 大的点(前 \(3\) 大是为了和除了这两个点以外的另外两个点区分开) 如果符合点不重复就加入即可

总的时间复杂度为 \(O(n^2)\) ( \(set\) 的复杂度约为常数 可以忽略 )

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
#define int long long
const int N = 2500 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;

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 , k , val[N] , q[10000000] , head , tail , dis[N][N] , vis[N] , maxx;

struct node { int id , val; friend bool operator < ( const node &a , const node &b ) { return a.val < b.val; } };
set<node> s[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); } 	

void bfs ( int s )
{
	memset ( dis[s] , inf , sizeof dis[s] );
	memset ( vis , 0 , sizeof vis );
	head = 1 , tail = 0;
	q[++tail] = s , dis[s][s] = 0 , vis[s] = 1;
	while ( head <= tail )
	{
		int u = q[head++];
		for ( auto v : e[u] )
			if ( dis[s][v] > dis[s][u] + 1 ) 
			{
				dis[s][v] = dis[s][u] + 1;
				if ( !vis[v] ) vis[v] = 1 , q[++tail] = v;
			}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	for ( int i = 2 ; i <= n ; i ++ ) val[i] = read();
	for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	for ( int i = 1 ; i <= n ; i ++ ) bfs ( i );
	for ( int i = 2 ; i <= n ; i ++ ) 	
		for ( int j = 2 ; j <= n ; j ++ )
			if ( i != j )
			{
				if ( dis[i][j] <= k + 1 && dis[1][j] <= k + 1 ) s[i].insert({j,val[j]});
				if ( s[i].size() > 3 ) s[i].erase(s[i].begin());
			}
	for ( int b = 2 ; b <= n ; b ++ ) 
		for ( int c = 2 ; c <= n ; c ++ )
			for ( auto [a,tmp1] : s[b] )
				for ( auto [d,tmp2] : s[c] )
					if ( a != b && a != c && a != d && b != c && b != d && c != d && dis[b][c] <= k + 1 )
						maxx = max ( maxx , val[a] + val[b] + val[c] + val[d] );
	cout << maxx << endl;
	return 0;
}

P8818 [CSP-S 2022] 策略游戏

对于小 \(L\) 的策略有四种情况:正数最大值 正数最小值 负数最大值 负数最大值

然后小 \(Q\) 相对应的策略就是小 \(L\) 策略的反向 经过讨论可得 记录整个 \(b\) 序列最大值和最小值即可

\(6\)\(st\) 表来分类讨论即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first 
#define se second
#define print(x) cout<<#x<<' '<<x<<endl
#define int long long 
#define getchar() cin.get()
const int N = 1e5 + 5;
const int M = 25;
const int inf = LONG_LONG_MAX;
// char buf[1<<24] , *p1 , *p2;
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)

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 , m , q;
int a[N] , b[N] , maxxa[N][M] , minna[N][M] , zminna[N][M] , fmaxxa[N][M] , maxxb[N][M] , minnb[N][M];



signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , q = read();
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read() , maxxa[i][0] = minna[i][0] = a[i];
		zminna[i][0] = ( a[i] >= 0 ) ? a[i] : inf , fmaxxa[i][0] = ( a[i] < 0 ) ? a[i] : -inf;
	}
	for ( int i = 1 ; i <= m ; i ++ ) b[i] = read() , maxxb[i][0] = minnb[i][0] = b[i];
	for ( int j = 1 ; j <= __lg(n) ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ )
		{
			maxxa[i][j] = max ( maxxa[i][j-1] , maxxa[i+(1<<j-1)][j-1] ); 
			minna[i][j] = min ( minna[i][j-1] , minna[i+(1<<j-1)][j-1] );
			zminna[i][j] = min ( zminna[i][j-1] , zminna[i+(1<<j-1)][j-1] );
			fmaxxa[i][j] = max ( fmaxxa[i][j-1] , fmaxxa[i+(1<<j-1)][j-1] );
		}
	for ( int j = 1 ; j <= __lg(m) ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= m ; i ++ )
		{
			maxxb[i][j] = max ( maxxb[i][j-1] , maxxb[i+(1<<j-1)][j-1] );
			minnb[i][j] = min ( minnb[i][j-1] , minnb[i+(1<<j-1)][j-1] );
		}
	for ( int i = 1 ; i <= q ; i ++ )
	{
		int l1 = read() , r1 = read() , l2 = read() , r2 = read();
		int k1 = __lg(r1-l1+1) , k2 = __lg(r2-l2+1);
		int maxa = max ( maxxa[l1][k1] , maxxa[r1-(1<<k1)+1][k1] );
		int mina = min ( minna[l1][k1] , minna[r1-(1<<k1)+1][k1] );
		int zmina = min ( zminna[l1][k1] , zminna[r1-(1<<k1)+1][k1] );
		int fmaxa = max ( fmaxxa[l1][k1] , fmaxxa[r1-(1<<k1)+1][k1] );
		int maxb = max ( maxxb[l2][k2] , maxxb[r2-(1<<k2)+1][k2] );
		int minb = min ( minnb[l2][k2] , minnb[r2-(1<<k2)+1][k2] );
		int ans = -inf;
		// print(maxa) , print(mina) , print(zmina) , print(fmaxa) , print(maxb) , print(minb);
		ans = max ( ans , maxa * ( maxa >= 0 ? minb : maxb ) );
		ans = max ( ans , mina * ( mina >= 0 ? minb : maxb ) );
		if ( zmina != inf ) ans = max ( ans , zmina * minb );
		if ( fmaxa != -inf ) ans = max ( ans , fmaxa * maxb );
		cout << ans << endl;
	}
	return 0;
}

P8819 [CSP-S 2022] 星战

\(70pts\) 暴力:数据分治

首先 记录每一个点的出度为 \(cd[i]\) 那么答案合法当且仅当 所有点的出度都为 \(1\)

对于 \(\le 1000\) 的点 我们可以直接对于四个操作依次遍历出边并标记出边(对于 \(2,4\) 操作建反向边即可) 统计出度即可

那么对于没有 \(2\) 操作和 \(4\) 操作的点 我们考虑用一个全局变量来记录不合法的点的个数来做到 \(O(q)\) 的复杂度

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first 
#define se second
#define print(x) cout<<#x<<' '<<x<<endl
// #define getchar() cin.get()
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

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 , m , q , cd[N] , ntot;

vector<pii> e[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }

void solve1()
{
	ntot = n;
	for ( int i = 1 , u , v ; i <= m ; i ++ )
	{
		u = read() , v = read();
		++ cd[u];
		if ( cd[u] == 1 ) -- ntot;
		if ( cd[u] == 2 ) ++ ntot;
	}
	q = read();
	for ( int i = 1 ; i <= q ; i ++ )
	{
		int op = read();
		if ( op == 2 || op == 4 ) { for ( int j = i ; j <= q ; j ++ ) cout << "NO" << endl; return; }
		if ( op == 1 )
		{
			int u = read() , v = read();
			-- cd[u];
			if ( cd[u] == 0 ) ++ ntot;
			if ( cd[u] == 1 ) -- ntot;
		}
		else 
		{
			int u = read() , v = read();
			++ cd[u];
			if ( cd[u] == 1 ) -- ntot;
			if ( cd[u] == 2 ) ++ ntot;
		}
		cout << ( ntot == 0 ? "YES" : "NO" ) << endl;
	}
}

void solve2()
{
	for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( v , u , 1 ) , ++ cd[u];//建立反向边 这样题中的入度就改为了出度
	q = read();
	for ( int i = 1 ; i <= q ; i ++ )
	{
		int op = read();
		if ( op == 1 )
		{
			int u = read() , v = read();
			for ( auto &p : e[v] ) if ( p.fi == u ) p.se = 0 , -- cd[u];
		}
		if ( op == 2 )
		{
			int u = read();
			for ( auto &p : e[u] ) if ( p.se != 0 ) p.se = 0 , -- cd[p.fi];
		}
		if ( op == 3 )
		{
			int u = read() , v = read();
			for ( auto &p : e[v] ) if ( p.fi == u ) p.se = 1 , ++ cd[u];
		}
		if ( op == 4 )
		{
			int u = read();
			for ( auto &p : e[u] ) if ( p.se == 0 ) p.se = 1 , ++ cd[p.fi];
		}
		int flag = 1;
		for ( int j = 1 ; j <= n ; j ++ ) if ( cd[j] != 1 ) flag = 0;
		cout << ( flag == 1 ? "YES" : "NO" ) << endl;
	}
}

/*
3 6
2 3
2 1
1 2
1 3
3 1
3 2
11
1 3 2
1 2 3
1 1 3
1 1 2
3 1 3
3 3 2
2 3
1 3 1
3 1 3
4 2
1 3 2
*/

signed main ()
{
// 	freopen("a.in" , "r" , stdin );
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	if ( n > 1000 ) solve1();
	else solve2();
	return 0;
}

P8820 [CSP-S 2022] 数据传输

数据分治打部分分()

\(k=1\) 显然树剖维护即可

\(n\le 2000\) \(dfs\) 先跑一遍两点距离 对于所有距离 \(\le k\) 的点对建边 跑一遍 \(dijkstra\) 即可 (\(n^2logn\)\(2000\) 确实很轻松 应该改一改观念了)

\(k=2\) 考虑 \(dp\)

在树上将这条链提出来拍成平面 然后设置 \(f[i]\) 表示链上到第 \(i\) 个点的答案 那么 \(f[i]\) 可以由前置合法状态转移过来 因为 \(1\) 节点一定会取到 那么我们起点从 \(1\) 开始即可

时间复杂度 \(O(nq)\)

三个数据点分治即为 \(64pts\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first 
#define se second
#define print(x) cout<<#x<<' '<<x<<endl
#define int long long 
#define getchar() cin.get()
// char buf[1<<24] , *p1 , *p2;
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)


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 , m , k;

namespace sub1
{
	const int N = 1e6 + 5;

	int a[N] , sum[N];
	int sz[N] , dep[N] , son[N] , fa[N];
	int timer , top[N] , rev[N] , pos[N];

	vector<int> e[N];
	inl void add ( int u , int v ) { e[u].eb(v); }

	struct LCA
	{
		void dfs1 ( int u , int ff )
		{
			sz[u] = 1 , dep[u] = dep[fa[u] = ff] + 1 , sum[u] = sum[ff] + a[u];
			for ( auto v : e[u] )
				if ( v ^ ff )
				{
					dfs1 ( v , u ); 
					sz[u] += sz[v];
					if ( sz[v] > sz[son[u]] ) son[u] = v;
				}
		}
		void dfs2 ( int u , int tp )
		{
			top[u] = tp , pos[u] = ++timer , rev[timer] = u;
			if ( son[u] ) dfs2 ( son[u] , tp );
			for ( auto v : e[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
		}
		int lca ( int u , int v ) 
		{
			while ( top[u] != top[v] )
			{
				if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
				u = fa[top[u]];
			}
			if ( dep[u] < dep[v] ) swap ( u , v );
			return v;
		}
		int query ( int u , int v )
		{
			int llca = lca ( u , v );
			return sum[u] + sum[v] - sum[llca] - sum[fa[llca]];
		}
	}T;

	void main()
	{
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
		T.dfs1 ( 1 , 0 ) , T.dfs2 ( 1 , 1 );
		for ( int i = 1 ; i <= m ; i ++ )
		{
			int u = read() , v = read();
			cout << T.query ( u , v ) << endl;
		}
	}
}

namespace sub2
{
	const int inf = 0x3f3f3f3f3f3f3f3f;
	const int N = 2e3 + 5;
	int dep[N][N] , dis[N][N] , vis[N] , a[N] , que[N*N];

	vector<int> e[N] , ee[N];
	inl void add ( int u , int v ) { e[u].eb(v); }
	inl void adde ( int u , int v ) { ee[u].eb(v); }

	struct node { int id , dis; friend bool operator < ( const node &a , const node &b ) { return a.dis > b.dis; } };
	priority_queue<node> q;
	void dij ( int s )
	{
		for ( int i = 1 ; i <= n ; i ++ ) dis[s][i] = inf;
		dis[s][s] = a[s];
		q.push ( (node) { s , a[s] } );
		while ( !q.empty() )
		{
			int u = q.top().id , ff = q.top().dis; q.pop();
			if ( ff != dis[s][u] ) continue;
			for ( auto v : ee[u] )
			{
				if ( dis[s][v] > dis[s][u] + a[v] )
				{
					dis[s][v] = dis[s][u] + a[v];
					q.push ( (node) { v , dis[s][v] } );
				}
			}
		}
	}

	void dfs ( int s )
	{
		for ( int i = 1 ; i <= n ; i ++ ) dep[s][i] = inf;
		memset ( vis , 0 , sizeof vis );
		int head = 1 , tail = 0;
		dep[s][s] = 0 , que[++tail] = s , vis[s] = 1;
		while ( head <= tail )
		{
			int u = que[head++];
			for ( auto v : e[u] )
				if ( dep[s][v] > dep[s][u] + 1 )
				{
					dep[s][v] = dep[s][u] + 1;
					if ( !vis[v] ) vis[v] = 1 , que[++tail] = v;
				}
		}
	}

	void main()
	{
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
		for ( int i = 1 ; i <= n ; i ++ ) dfs ( i );
		for ( int i = 1 ; i <= n ; i ++ )	
			for ( int j = 1 ; j <= n ; j ++ )
				if ( i != j && dep[i][j] <= k ) adde ( i , j );
		for ( int i = 1 ; i <= n ; i ++ ) dij ( i );
		for ( int i = 1 ; i <= m ; i ++ )
		{
			int u = read() , v = read();
			cout << dis[u][v] << endl;
		}
	}
}

namespace sub3
{
	const int inf = 0x3f3f3f3f3f3f3f3f;
	const int N = 1e6 + 5;

	int a[N] , sum[N] , sta[N] , topp , f[N];
	int sz[N] , dep[N] , son[N] , fa[N];
	int timer , top[N] , rev[N] , pos[N];

	vector<int> e[N];
	inl void add ( int u , int v ) { e[u].eb(v); }

	struct LCA
	{
		void dfs1 ( int u , int ff )
		{
			sz[u] = 1 , dep[u] = dep[fa[u]=ff] + 1 , sum[u] = sum[ff] + a[u];
			for ( auto v : e[u] )
				if ( v ^ ff )
				{
					dfs1 ( v , u ); 
					sz[u] += sz[v];
					if ( sz[v] > sz[son[u]] ) son[u] = v;
				}
		}
		void dfs2 ( int u , int tp )
		{
			top[u] = tp , pos[u] = ++timer , rev[timer] = u;
			if ( son[u] ) dfs2 ( son[u] , tp );
			for ( auto v : e[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
		}
		int lca ( int u , int v ) 
		{
			while ( top[u] != top[v] )
			{
				if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
				u = fa[top[u]];
			}
			if ( dep[u] < dep[v] ) swap ( u , v );
			return v;
		}
	}T;

	int solve ( int u , int v )
	{
		int L = T.lca ( u , v );
		int uu = u , vv = v , temp;
		topp = 0;
		while ( uu != L ) sta[++topp] = uu , uu = fa[uu];
		sta[++topp] = L; temp = topp;
		while ( vv != L ) sta[++topp] = vv , vv = fa[vv];
		reverse ( sta + temp + 1 , sta + topp + 1 );
		for ( int i = 1 ; i <= topp ; i ++ ) f[i] = inf;
		f[1] = a[sta[1]];
		for ( int i = 2 ; i <= topp ; i ++ )
			for ( int j = 1 ; j <= min ( i - 1 , k ) ; j ++ )
				f[i] = min ( f[i] , f[i-j] + a[sta[i]] );
		return f[topp];
	}

	void main ()
	{
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
		T.dfs1 ( 1 , 0 ) , T.dfs2 ( 1 , 1 );
		for ( int i = 1 ; i <= m ; i ++ )
		{
			int u = read() , v = read();
			cout << solve ( u , v ) << endl;
		}
	}
}

/*
7 3 3
1 2 3 4 5 6 7
1 2
1 3
2 4
2 5
3 6
3 7
4 7
5 6
1 2
*/

signed main ()
{
	// freopen ( "transmit3.in" , "r" , stdin );
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	if ( k == 1 ) sub1::main();
	else if ( k == 2 ) sub3::main();
	else if ( n <= 2000 ) sub2::main(); 
	return 0;
}
posted @ 2023-10-11 19:29  Echo_Long  阅读(17)  评论(0)    收藏  举报