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


    (CSP-S2020)合集

    A. 儒略日


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

    这道题实际上模拟的话可以分为公元前1582.10.41582.10.5~1600.12.311600.12.31以后,为什么要分到$$1600.12.31$$为结点呢?后面每四百年蹦一次方便。


    当然,我们也可以通过观察样例求解:

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

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<ctime>
    #include<set>
    #include<map>
    #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 C = 3146097, T = 146097, E = 3000000;
    
    int D[C + 7], M[C + 7], Y[C + 7];
    
    void init()
    {
    	int day = 1, mon = 1, year = -4713;
    	bool op = true;
    	D[0] = 1, M[0] = 1, Y[0] = -4713;
    	FOR(i, 1, C)
    	{
    		++ day;
    		if(mon == 2) if((op && (day == 30)) || (op == false && (day == 29))) ++ mon, day = 1;
    		if(mon == 1 || mon == 3 || mon == 5 || mon == 7 || mon == 8 || mon == 10 || mon == 12) 
    			if(day == 32) ++ mon, day = 1;
    		if(mon == 4 || mon == 6 || mon == 9 || mon == 11) if(day == 31) ++ mon, day = 1;
    		if(mon == 13) ++ year, mon = 1;
    		if(year == 0) year = 1;
    		if(year == 1582 && mon == 10 && day == 5) day = 15;
    		D[i] = day, M[i] = mon, Y[i] = year;
    		if(year < 0) op = (-year) % 4 == 1 ? true : false;
    		else 
    		{
    			if(year < 1583) op = year % 4 == 0 ? true : false;
    			else op = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)? true:false;
    		}
    	}
    	return;
    }
    long long r;
    int main()
    {
    	int q;
    	scanf("%d", &q);
    	init();
    	while(q --)
    	{
    		scanf("%lld", &r);
    		if(r <= C) 
    		{
    			printf("%d %d %d", D[r], M[r], abs(Y[r]));
    			if(Y[r] < 0) printf(" BC
    ");
    			else puts("");
    		}
    		else
    		{
    			int tmp = E + (r - E) % T;
    			printf("%d %d %d
    ", D[tmp], M[tmp], Y[tmp] + (r - E) / T * 400);
    		}
    	}
    	return 0;
    }
    

    B. 动物园


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

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #include<string>
    #include<cstdio>
    #include<cmath>
    #include<bitset>
    #include<queue>
    #include<map>
    #include<set>
    #define ull unsigned long long
    using namespace std;
    const int N = 1000000 + 5;
    bitset <100000000 + 5> table;
    struct query
    {
    	int p, q;
    } Q[N];
    int n, m, c, k;
    ull a[N]; 
    bool book[71] = {};
    inline void read(ull &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 calc(ull x)
    {
    	int res = 0;
    	while(x)
    	{
    		if(x & 1) ++ res;
    		x >>= 1;
    	}
    	return res;
    }
    int main()
    {
    	table.reset();
    	scanf("%d %d %d %d", &n, &m, &c, &k);
    	ull P = 0;
    	for(int i = 1; i <= n; ++ i) read(a[i]);
    	for(int i = 1; i <= n; ++ i) P |= a[i];
    	for(int i = 0; i < m; ++ i)
    	{
    		scanf("%d %d", &Q[i].p, &Q[i].q);
    		if((P >> Q[i].p) & 1) table[Q[i].q] = true; 
    	}
    	ull res = 0;
    	for(int i = 0; i < m; ++ i)
    	{
    		if(table[Q[i].q]) res |= 1ll << Q[i].p;
    		else book[Q[i].p] = true;
    	}
    	bool valid = false;
    	for(int i = 0; i < k; ++ i)
    	{
    		if(!book[i]) 
    		{
    			res |= 1ll << i;
    		}
    		else valid = true;
    	}
    	if(valid || k < 64) 
    	{
    		int cnt = calc(res);
    		printf("%lld
    ", (1ll << cnt) - n);
    	} 
    	else printf("18446744073709551616
    ");
    	return 0;
    }
    

    C. 函数调用


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

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

    #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,按照拓扑序计算出每一个加法操作被调用次数,对于每一个加法操作函数,可以记录它更新的位置,统计时将该位置上的数加上总次数即可。

    乘法操作虽则如出一辙,但我们对于该问题,有不一样的解法。考虑计算每一个函数所产生的乘积贡献值,将所有调用的函数乘积贡献值(即(dp1[i]))统计一下即可。


    我们将问题转化:对于每一个数(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 = 1000000 + 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;
    }
    
    struct Query
    {
    	int type, P, V;
    	int C;
    } q[N];
    vector <int> G[N];
    queue <int> Q;
    int n, m, T, f[M], deg[N];
    LL mul = 1, a[N], add[N], prob[N], dp[N];// dp stands for the times that a function has been called 
    void dfs(int u)
    {
    	if(prob[u] != -1) return;
    	prob[u] = 1;
    	if(q[u].type == 1) return;
    	if(q[u].type == 2) 
    	{ 
    		prob[u] = q[u].V;
    		return;
    	}
    	reverse(G[u].begin(), G[u].end());
    	int v;
    	for(int i = 0; i < G[u].size(); ++ i)
    	{
    		v = G[u][i];
    		dfs(v);
    		prob[u] = 1ll * prob[u] * prob[v] % mod;
    	}
    	return;
    }
    int main()
    {
    	read(n);
    	FOR(i, 1, n) read(a[i]);
    	read(m);
    	FOR(i, 1, m) G[i].clear();
    	CLR(deg, 0);
    	int tmp;
    	FOR(i, 1, m)
    	{
    		read(q[i].type);
    		switch(q[i].type)
    		{
    			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)
    				{
    					read(tmp);
    					G[i].push_back(tmp);
    					++ deg[tmp];
    				}
    				break;
    			default: break;
    		}
    	}
    	CLR(prob, -1);
    	FOR(i, 1, m) dfs(i); // calculate the product of each function
    	read(T);
    	FOR(i, 1, T) read(f[i]);
    	int u, v;
    	LL w;
    	ROF(i, T, 1) 
    	{
    		u = f[i];
    		dp[u] = (dp[u] + mul) % mod;
    		if(q[u].type == 2 || q[u].type == 3) mul = 1ll * prob[u] * mul % mod;
    	}
    	while(Q.size()) Q.pop();
    	
    	FOR(i, 1, m) 
    		if(!deg[i]) Q.push(i);
    
    	while(Q.size())
    	{
    		u = Q.front(); Q.pop();
    		if(q[u].type == 1) add[q[u].P] = (add[q[u].P] + 1ll * dp[u] * q[u].V % mod) % mod; 
    		w = dp[u];
    		for(int i = 0; i < G[u].size(); ++ i)
    		{
    			v = G[u][i];
    			-- deg[v];
    			if(!deg[v]) Q.push(v);
    			dp[v] = (dp[v] + w) % mod;
    			w = 1ll * w * prob[v] % mod;
    		}
    	}
    	FOR(i, 1, n) printf("%d ", (1ll * a[i] * mul % mod + add[i]) % mod);
    	puts("");
    	return 0;
    } 
    
  • 相关阅读:
    高级语言发展之回归人类思维——听老赵的Session有感
    走进单元测试(1):为什么难以广泛应用?
    梦话对象之三:三要素的差异与统一
    走进单元测试(2):必须要自动化
    缺乏自信怎么办?
    梦话对象之一:逃不开的生死问题
    走进单元测试(3):消灭HttpContext的依赖,兼谈单元测试的设计辅助性
    我也想对广大程序员说一些话
    梦话对象之二:事件之无限扩展
    《JavaScript高级程序设计》学习笔记——错误处理与调试
  • 原文地址:https://www.cnblogs.com/zach20040914/p/14074482.html
Copyright © 2020-2023  润新知