CSP-S 2019 第二轮 DAY1 简单解析(含代码)

第一题:格雷码,构造,逆向思维

第二题:括号树,括号序列,栈,dfs,构造

第三题:树上的数,贪心,链表,构造

 

D1T1:格雷码

思路:第一眼看到格雷码感觉格外亲切,年纪大了,熬夜熬不动了,记忆力也衰退了不少,隐约记得格雷码曾经在某一次初赛的试题里出现过,当然也可能是模拟赛。包括SCOI 2015年还有一题《超级格雷码》,腾讯2016年笔试有一题是生成格雷码,LeetCode (一个面试海外IT公司很有用的题库)也有格雷码的题目,格雷码和二进制之间的转换应该是一个经典问题。

回归到本题当中,本题充分考察了选手逆向思维,如果80%的数据,采用构造格雷码的方法,那么根据k反推格雷码的规律也应该能够想出来。如果K的当前位为1,则格雷码该位也为1,然后把K剩下未处理的位取反,如果K的当前位为0,则对应的格雷码也为0,但不取反, 可以结合代码理解,建议动动笔推导一下。

#include<iostream>
#include<cstdio>

using namespace std;

int main() {
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    unsigned long long n, k;
    cin >> n >> k;

    for(long long i = n-1; i >= 0; --i) {
        if ((k >> i)&1) {
            cout << "1";
            k = ~k;
        } else {
            cout << "0";
        }
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

 

D1T2: 括号树

思路:括号序列和树上问题结合,点赞。其次特殊性质fi = i - 1 是一条链了,都是很好的得部分分的性质,链上合法括号序列很简单,所以这题没有50分以上的选手都值得反思。

括号序列如果你能想到stack然后计算对答案的贡献,其实还是可做的,首先建树从树根往下跑dfs,用栈来记录途中的括号,再考虑到一个位置对答案有贡献,能贡献的肯定是右括号,过程中记录待匹配的左括号即可,注意叠加即可,如果你想清楚链的问题,相信树的问题也能想通。所以本题仍旧是一个树的经典问题,如何在dfs过程中维护我们想要的数据。

注意结果开long long,在oitiku上试了一下不开longlong,只有50分。。。如果真的是longlong卡50分,还不如在题目里提示一下大家结果超int,RP++

#include <iostream>
#include <cstdio>
#include <stack>

using namespace std;
const int maxn = 500000 + 10;

struct Edge {
	int v, next;
} e[2 * maxn];

int n, fa[maxn], f[maxn], cnt[maxn], tot = 0; 
string s;
stack<int> stk;
long long ans = 0, current = 0;

void add(int u, int v) {
	tot += 1;
	e[tot].v = v;
	e[tot].next = f[u];
	f[u] = tot;
}

void dfs(int x) {
	int cur = 0;
	if (!stk.empty() && s[stk.top() - 1] == '(' && s[x - 1] == ')') {
		cur = stk.top();
		stk.pop();
		cnt[x] = cnt[fa[cur]] + 1;
	} else {
		cnt[x] = 0;
		stk.push(x);
	}

	current += cnt[x];
	ans = ans ^ (x * current);

	for (int i = f[x]; i > 0; i = e[i].next) {
		int v = e[i].v;
		dfs(v);
	}
	current -= cnt[x];

	if (cur)
		stk.push(cur);
	else
		stk.pop();

 }
int main() {
	freopen("brackets.in","r",stdin);
    freopen("brackets.out","w",stdout);
	cin >> n;
	cin >> s;
	
	for (int i = 2; i <= n; ++i) {
		scanf("%d", &fa[i]);
		add(fa[i], i);
	} 
	dfs(1);
	cout << ans << endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}



 

D1T3:树上的数

题目大意是带权树,n-1条边,删边会调换连接这条边的两个点的权值,最后按点权排序,求结点编号的字典序最小的解。本题难度是有点大,但部分分给的也不少。

思路:考试时长3.5小时的情况下,第三题有链,有菊花图(n-1度的点是大菊花)拿个60分,岂不是DAY1 260还是美滋滋的。不少能力出色的选手由于无法驾驭自己的能力,导致最后还不如专心写部分分,这样的例子每年都数不胜数。链和菊花图本题给分数不低,且作为突破口思考是非常值得的。对于普通选数从构思到写和调试,一共3.5小时还是有点紧。

根据字典序最小,前面尽可能的小,大概能摸到一些贪心的思路。

本题出人已经在知乎上给出做法了,写的很详细:

https://www.zhihu.com/question/351347604/answer/896344216

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
struct Edge
{
     int u, v, next;
} e[2005 * 2];

int n, p[2005];

int pre[2005][2005], nxt[2005][2005];
int cnt[2005], f[2005], ans[2005], tot = 0;

void add(int u, int v)
{
     tot += 1;
     e[tot].u = u;
     e[tot].v = v;
     e[tot].next = f[u];
     f[u] = tot;
}

bool check(int l, int m, int r)
{
     if (pre[m][r] == -1)
          return false;
     if (nxt[m][l] == -1)
          return false;
     if (nxt[m][l] == r)
          return false;
     if (nxt[m][l] == 0 && pre[m][r] == n + 1 && cnt[m] != 2)
          return false;

     return true;
}

void un(int l, int m, int r)
{
     nxt[m][pre[m][r]] = nxt[m][l];
     pre[m][nxt[m][l]] = pre[m][r];
     pre[m][r] = nxt[m][l] = -1;
     cnt[m]--;
}

bool remove(int x, int y, int fa)
{
     if (x == y)
     {
          un(fa, x, n + 1);
          return true;
     }
     for (int i = f[x]; i; i = e[i].next)
     {
          int v = e[i].v;
          if (v == fa)
               continue;
          if (remove(v, y, x))
          {
               un(fa, x, v);
               return true;
          }
     }
     return false;
}

int find(int x, int fa)
{
     int result = n;
     if (check(fa, x, n + 1) && x < n)
          result = x;

     for (int i = f[x]; i; i = e[i].next)
     {
          int v = e[i].v;
          if (v == fa)
               continue;
          if (check(fa, x, v))
               result = min(result, find(v, x));
     }
     return result;
}

void work()
{
     cin >> n;
     tot = 0;
     for (int i = 0; i <= n; ++i)
          f[i] = 0;

     for (int i = 1; i <= n; ++i)
          scanf("%d", &p[i]);
     for (int i = 1; i <= n; ++i)
     {
          for (int j = 0; j <= n + 2; ++j)
               pre[i][j] = nxt[i][j] = j;
     }

     for (int i = 1; i <= n; ++i)
          cnt[i] = 2;
     for (int i = 1; i < n; ++i)
     {
          int u, v;
          scanf("%d%d", &u, &v);
          add(u, v);
          add(v, u);
          cnt[u] += 1;
          cnt[v] += 1;
     }

     for (int i = 1; i <= n; ++i)
     {
          int np = find(p[i], 0);
          ans[i] = np;
          remove(p[i], np, 0);
     }

     for (int i = 1; i < n; ++i)
          printf("%d ", ans[i]);
     printf("%d\n", ans[n]);
}

int main()
{
     freopen("tree.in", "r", stdin);
     freopen("tree.out", "w", stdout);
     int T;
     cin >> T;
     while (T--)
     {
          work();
     }
     return 0;
     fclose(stdin);
     fclose(stdout);
}


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章