CSP-S2020解题报告(待完成!)

\(CSP-S2020\)合集

A. 儒略日


这道题模拟能有\(80pts\),但是考场上我不看题,花了将近三个小时敲大模拟\(40pts\)ios

这道题实际上模拟的话能够分为公元前1582.10.41582.10.5~1600.12.311600.12.31之后,为何要分到$$1600.12.31$$为结点呢?后面每四百年蹦一次方便。c++


固然,咱们也能够经过观察样例求解:数组

考虑到\(P=3000000\)天时刚好是\(3501.8.15\),再日后蹦\(400\)年要\(T\)\(146097\)天。暴力预处理这些天数。而后小于等于\(C=3146097\)的天数直接输出,大于的年月用\(C+r\% T\)天数输出,年份\((r/T)\)加上\((P+r\%T)\)表明的年份便可。函数


B. 动物园


无语,居然忘记特判了。\(1\)\(64\)位就谁也救不了我了。优化


C. 函数调用


考场上不看题,暴力分都没拿到。spa

这道题模拟、暴力\(20pts\)。更优地,咱们甚至能够将乘法累积到变量,加法乘该积数逆用于优化常数。code

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#define CLR(x, y) memset(x,y,sizeof(x))
#define FOR(i, x, y) for(register int i=x;i<=y;++i)
#define ROF(i, x, y) for(register int i=x;i>=y;--i)
#define pii pair<int,int>
using namespace std;
const int N = 200000 + 5, mod = 998244353;
struct Query
{
	int t, p, v, c;
	vector <int> g;
} q[N];

int n, m, Q, a[N], f[N];
template <typename T> void read(T &x)
{
	bool mark = false;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
	for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
	if(mark) x = -x;
	return;	
}
void work(int p)
{
	if(q[p].t == 1) a[q[p].p] = (a[q[p].p] + q[p].v) % mod;
	else if(q[p].t == 2) FOR(i, 1, n) a[i] = 1ll * a[i] * q[p].v % mod;
	else FOR(i, 0, q[p].g.size() - 1) work(q[p].g[i]);
	return;
}
int main()
{
	read(n);
	FOR(i, 1, n) read(a[i]);
	read(m);
	FOR(i, 1, m)
	{
		read(q[i].t);
		switch(q[i].t)
		{
			case 1:
			{
				read(q[i].p), read(q[i].v);
				break;
			}
			case 2:
			{
				read(q[i].v);
				break;
			}
			case 3:
			{
				read(q[i].c);
				FOR(j, 1, q[i].c) 
				{
					int tmp;
					read(tmp);
					q[i].g.push_back(tmp);
				}
				break;
			}
		}
	}
	read(Q);
	FOR(i, 1, Q) read(f[i]);
	FOR(i, 1, Q) work(f[i]);
	FOR(i, 1, n) printf("%d ", a[i]);
	puts("");
	return 0;
}

假设操做序列中只有加法或乘法操做,那么调用顺序毫无影响。排序

对于加法操做,咱们只须要把每一个函数对序列的影响记录下来,计算每一位上的数的变量。具体来讲,咱们能够根据调用关系创建一张DAG,按照拓扑序计算出每个加法操做被调用次数,对于每个加法操做函数,能够记录它更新的位置,统计时将该位置上的数加上总次数便可。get

乘法操做虽则一模一样,但咱们对于该问题,有不同的解法。考虑计算每个函数所产生的乘积贡献值,将全部调用的函数乘积贡献值(即\(dp1[i]\))统计一下便可。string


咱们将问题转化:对于每个数\(a_i\),先不考虑加的数对它的影响,它最终的答案就是总乘积乘该数。那么,该问题变为了对于每个加法操做对整个序列的影响统计,能够考虑模拟的整个过程。

该过程当中,会发现,设第\(f_i\)个为加法操做,那么,对于后面的全部的\(f_j(j>i)\),它们的乘法贡献必定会做用于第\(f_i\)个操做的值,最后统计便可。也就是说\(val*mul\)

为了可以统计后面的乘积,使其可以对前面有影响,咱们倒序进行。

时间复杂度下降为\(O(Q*m)\)

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#define CLR(x, y) memset(x,y,sizeof(x))
#define FOR(i, x, y) for(register int i=x;i<=y;++i)
#define ROF(i, x, y) for(register int i=x;i>=y;--i)
using namespace std;

const int N = 100000 + 5, M = 100000 + 5, mod = 998244353;

typedef long long LL;

vector <int> G[M];

int n, m, f[N], p[M], type[M];
LL a[N], mul = 1, v[M], add[N];
void dfs(int u)
{
	if(type[u] == 1) 
	{
		add[p[u]] = (add[p[u]] + 1ll * v[u] * mul % mod) % mod;
		return;
	}
	if(type[u] == 2)
	{
		mul = 1ll * mul * v[u] % mod;
		return;
	}
	int v;
	for(int i = G[u].size() - 1; i >= 0; -- i)
	{
		v = G[u][i];
		dfs(v);
	}
	return;	
}

template <typename T> void read(T &x)
{
	bool mark = false;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
	for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
	if(mark) x = -x;
	return;
}
int main()
{
	read(n);
	FOR(i, 1, n) read(a[i]);
	read(m);
	FOR(i, 1, m) G[i].clear();
	FOR(i, 1, m)
	{
		read(type[i]);
		switch(type[i])
		{
			case 1:
				read(p[i]), read(v[i]);
				break;
			case 2:
				read(v[i]);
				break;
			case 3:
				int tmp;
				read(tmp);
				G[i].resize(tmp);
				FOR(j, 0, tmp - 1) read(G[i][j]);
				break;
			default: break;
		}
	}
	int T;
	read(T);
	FOR(i, 1, T) read(f[i]);
	CLR(add, 0);
	int u;
	ROF(i, T, 1) 
	{
		u = f[i];
		if(type[u] == 1) add[p[u]] = (add[p[u]] + v[u] * mul) % mod;
		else if(type[u] == 2) mul = 1ll * mul * v[u] % mod;
		else dfs(u);
	}
	FOR(i, 1, n) printf("%lld ", (1ll * a[i] * mul % mod + add[i]) % mod);
	puts("");
	return 0;
}

这样作并非最好的。

考虑:一个加法被乘\(k\)次,至关于该函数被调用\(k\)

而函数的“调用函数”被调用的次数须要累加到该函数内。

定义一个数组\(dp[u]\)表明结点被调用了几回。

若是该节点是个乘法调用,不用理它,由于乘法咱们已经计算完了;若是是一个“调用函数”那么,接下来的步骤,就是来解决内部的函数调用的统计;若是是一个加法调用,能够累加到\(add[i]\)\(i\)影响。

接下来,咱们统计全部在“调用函数”中被调用的函数调用次数。

拓扑排序。

不过,在TOP过程当中,要随时记录乘积,更新调用次数;另外,对于一个“调用函数”的调用顺序,能够倒序求解。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#define CLR(x, y) memset(x,y,sizeof(x))
#define FOR(i, x, y) for(register int i=x;i<=y;++i)
#define ROF(i, x, y) for(register int i=x;i>=y;--i)
using namespace std;

const int N = 100000 + 5, M = 100000 + 5, mod = 998244353;

typedef long long LL;


template <typename T> void read(T &x)
{
	bool mark = false;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
	for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
	if(mark) x = -x;
	return;
}
vector <int> G[M];
queue <int> Q;

int n, m, f[N], p[M], type[M], deg[M];
LL a[N], mul = 1, v[M], dp1[M], dp[M], add[N];
void dfs(int u)
{
	if(dp1[u] != -1) return;
	if(type[u] == 1) dp1[u] = 1;
	else if(type[u] == 2) dp1[u] = v[u];
	else
	{
		dp1[u] = 1;
		int x;
		for(int i = 0; i < G[u].size(); ++ i)
		{
			x = G[u][i];
			dfs(x);
			dp1[u] = 1ll * dp1[u] * dp1[x] % mod;
		}
	}
}
int main()
{
	read(n);
	FOR(i, 1, n) read(a[i]);
	read(m);
	CLR(deg, 0);
	FOR(i, 1, m) G[i].clear();
	FOR(i, 1, m)
	{
		read(type[i]);
		switch(type[i])
		{
			case 1:
				read(p[i]), read(v[i]);
				break;
			case 2:
				read(v[i]);
				break;
			case 3:
				int tmp;
				read(tmp);
				G[i].resize(tmp);
				FOR(j, 0, tmp - 1)
				{
					read(G[i][j]);
					++ deg[G[i][j]];
				}
				break;
			default: break;
		}
	}
	CLR(dp1, -1), CLR(dp, 0);
	FOR(i, 1, m) if(!deg[i]) dfs(i);
	int u, T;
	LL w;
	read(T);
	FOR(i, 1, T) read(f[i]);
	ROF(i, T, 1) 
	{
		u = f[i];
		if(type[u] == 1) dp[u] = (dp[u] + mul) % mod;
		else if(type[u] == 2) mul = 1ll * mul * dp1[u] % mod;
		else dp[u] = (dp[u] + mul) % mod, mul = 1ll * mul * dp1[u] % mod;
	}
	CLR(add, 0);
	while(Q.size()) Q.pop();
	FOR(i, 1, m) if(!deg[i]) Q.push(i);
	while(Q.size())
	{
		u = Q.front();
		Q.pop();
		if(type[u] == 1) add[p[u]] = (add[p[u]] + dp[u] * v[u]) % mod;
		w = dp[u];
		reverse(G[u].begin(), G[u].end());
		for(int i = 0; i < G[u].size(); ++ i)
		{
			int x = G[u][i];
			-- deg[x];
			dp[x] = (dp[x] + w) % mod;
			w = 1ll * w * dp1[x] % mod;
			if(!deg[x]) Q.push(x);
		}
	}
	FOR(i, 1, n) printf("%lld ", (1ll * a[i] * mul % mod + add[i]) % mod);
	puts("");
	return 0;
}
相关文章
相关标签/搜索