[CSP2020]函数调用

题目

点这里看题目。

分析

考场上觉得很难,考完发现 T4 才是最难的。

显然有:每个位置的值最终一定是一次函数形式:\(f_i(x)=kx+b_i\) 。我们可以直接算出 \(k\) ,那么我们只需要想办法求出 \(b_i\)

对于一个加法函数而言,每次加的值是固定的,因此我们只需要计算这个值之前的一个系数。

对于加法函数 \(u\) ,我们设它的系数为 \(p_u\) 。那么 \(p_u\) 即为所有在 \(u\) 之后调用的乘法函数之积。

一个函数 \(u\) 可能会被多次调用,只需要将每次调用的系数求和即可。我们只需要考虑单次调用的情况。

在单次被调用中,我们考虑该次操作直接调用的函数 \(f\) 。那么 \(p_u\) 可以拆分成两部分:调用 \(f\) 的时候调用的,且在 \(u\) 之后被执行的乘法函数的积\(f\) 之后被操作调用的导致的积

后一个部分很好处理,我们可以直接计算操作的后缀积。然后发现,其实前一个部分也比较简单。我们只需要将原先的调用顺序倒过来处理,同时维护当前节点的计算次数即可。

时间复杂度就是线性的。

小结:

  1. 答案的一次函数形式,将问题缩小到了加法上面。
  2. 利用 \(p\) 与时间相关的特性,将它与后缀积挂钩;同时将后缀拆分成两个部分。可以发现这就是不断缩减问题范围的过程。

代码

#include <cstdio>
#include <vector>
using namespace std;

typedef long long LL;

const int mod = 998244353;
const int MAXN = 1e5 + 5, MAXM = 1e6 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

vector<int> que[MAXN], Graph[MAXN];

int add[MAXN];

int func[MAXN], suf[MAXN];
int q[MAXN];

int T[MAXN], P[MAXN], V[MAXN], mul[MAXN], extra[MAXN];
int into[MAXN];
int a[MAXN];
int N, M, Q, cnt;

int Sub( int x, int v ) { return x < v ? x + mod - v : x - v; }
int Mul( LL x, int v ) { x *= v; if( x >= mod ) x %= mod; return x; }
int Add( int x, int v ) { return x + v >= mod ? x + v - mod : x + v; }

void AddEdge( const int from, const int to )
{
	Graph[from].push_back( to ), into[to] ++;
}

void Init()
{
	int h = 1, t = 0, u, v;
	for( int i = 1 ; i <= M ; i ++ )
		if( ! into[i] ) q[++ t] = i;
	while( h <= t )
	{
		u = q[h ++];
		for( int i = 0 ; i < ( int ) Graph[u].size() ; i ++ )
			if( ! ( -- into[v = Graph[u][i]] ) )
				q[++ t] = v;
	}
	for( int i = M ; i ; i -- )
	{
		int u = q[i]; mul[u] = 1;
		if( T[u] == 2 ) mul[u] = V[u];
		if( T[u] == 3 )
			for( int k = 0 ; k < ( int ) Graph[u].size() ; k ++ )
				mul[u] = Mul( mul[u], mul[Graph[u][k]] );
	}
}

int main()
{
	read( N );
	for( int i = 1 ; i <= N ; i ++ ) read( a[i] );
	read( M );
	for( int i = 1 ; i <= M ; i ++ )
	{
		read( T[i] );
		if( T[i] == 1 ) read( P[i] ), read( V[i] );
		if( T[i] == 2 ) read( V[i] );
		if( T[i] == 3 )
		{
			int C, v; read( C );
			while( C -- ) read( v ), AddEdge( i, v );
		}
	}
	Init(), read( Q );
	for( int i = 1 ; i <= Q ; i ++ )
		read( func[i] ), que[func[i]].push_back( i );
	suf[Q + 1] = 1; for( int i = Q ; i ; i -- ) suf[i] = Mul( suf[i + 1], mul[func[i]] );
	for( int i = 1, u, v ; i <= M ; i ++ )
	{
		u = q[i];
		for( int k = 0 ; k < ( int ) que[u].size() ; k ++ )
			extra[u] = Add( extra[u], suf[que[u][k] + 1] );
		if( T[u] == 1 ) add[P[u]] = Add( add[P[u]], Mul( V[u], extra[u] ) );
		else if( T[u] == 3 )
			for( int k = ( int ) Graph[u].size() - 1 ; ~ k ; k -- )
			{
				v = Graph[u][k];
				extra[v] = Add( extra[v], extra[u] );
				extra[u] = Mul( extra[u], mul[v] );
			}
	}
	for( int i = 1 ; i <= N ; i ++ ) write( Add( Mul( suf[1], a[i] ), add[i] ) ), putchar( i == N ? '\n' : ' ' );
	return 0;
}
posted @ 2020-11-15 09:12  crashed  阅读(202)  评论(0编辑  收藏  举报