1. 程式人生 > 實用技巧 >NOIp提高組/CSP-S 2019 Day1 試做

NOIp提高組/CSP-S 2019 Day1 試做

NOIp提高組/CSP-S 2019 DAY1 試做

按學長說的,刷一刷 NOIp 的題,然後就從洛谷裡直接找到了2019年的題,按順序先刷DAY1的,結果做到一半才想起來,去年的題好像不大對勁,嚴格說應該是CSP-S 2019……算了都已經開始了,至少把 D1 的先做了吧……

D1T1 格雷碼

題目描述

下方傳送門
題目連結
上方傳送門

思路分析

  • zz找規律題,硬是傻眼了半天。
  • 還是得先好好讀題。對於每次我們得出的格雷碼,都可以分兩半組成,前一半都是 \(0\) 開頭的,而後一半都是 \(1\) 開頭的。以這個規律為突破口。
  • 列舉一下 \(n=4\) 時的答案,大概是這樣的
0000 0001 0011 0010 0110 0111 0101 0100
1100 1101 1111 1110 1010 1011 1001 1000
  • 這樣就不難想到將格雷碼分而治之(即分治),依循上面的規律已經得出第一位,那麼第二位發現稍微有了一些變化。發現如果我們從後半段進行分治的話,規律正好反了過來,但其實大同小異
  • 雖然是D1T1,但做起來並沒想象中的輕鬆,後來大致掃了一下題解,好像沒幾個我這麼做的,都是一些奇奇怪怪的規律。
  • 另外求平方時別用左移別用左移!!!因為最高只能左移32位,就這破東西卡了我半個小時改來改去,然後我輸出了一下1<<43的結果發現事情不大對勁。

Code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll unsigned long long //2^64 long long會炸
using namespace std;
inline ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
ll n,k;
int main(){
	n = read(),k = read();
	bool flag = 1; //flag標記是否需要倒轉規律
	ll res = pow(2,n-1); 
	while(res){
		if(k>=res){
			flag?putchar('1'),flag=0:putchar('0');
			k-=res;
		}
		else flag?putchar('0'):putchar('1'),flag=1;
		res>>=1;
	}
	return 0;
}

D1T2 括號樹

下方傳送門
題目連結
上方傳送門

思路分析

  • 這個沒啥可說的……括號匹配當然要想到壓棧彈棧,只是放樹上了,並無大礙
  • 個人感覺T1比這題坑……

詳見程式碼

Code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 500010
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,fa[N],head[N],sum[N],pre[N],sta[N],top;
char s[N];
struct edge{
	int to,next;
}e[N];
int len;
void addedge(int u,int v){
	e[++len].to = v;
	e[len].next = head[u];
	head[u] = len;
}
void dfs(int u){
	int res = 0;
	if(s[u]=='(')sta[++top] = u; //壓進棧裡等待匹配
	else{ //遇到')'就可以從棧裡拿出括號來匹配了
		if(top){
			res = sta[top];
			pre[u] = pre[fa[res]]+1; //再左括號的父親的基礎上又增加了1對
			top--;
		}
	}
	sum[u] = sum[fa[u]]+pre[u];
	for(int i = head[u];i;i=e[i].next){
		int v = e[i].to;
		dfs(v);
	}
	if(res)sta[++top] = res; //記得再放回去,不然你會像我一樣獲得85分的好成績
	else if(top)top--; //要是未從棧內拿出元素且壓入了一個左括號,遞迴完了就需要彈出去,因為前面的點用不到
}
signed main(){
	n = read();
	scanf("%s",s+1);
	for(int i = 2;i <= n;i++){
		fa[i] = read();
		addedge(fa[i],i);
	}
	dfs(1);
	int ans = 0;
	for(int i = 1;i <= n;i++)ans ^= i*sum[i];
	printf("%lld\n",ans);
	return 0;
}

D1T3

下方傳送門
題目連結
上方傳送門

  • 這tm是道黑題???看了看題,目測只會打10分暴力,做個球,咕了咕了。

總結

  • T1 題目那麼長看著挺唬人的,而且乍一眼看上去也沒什麼思路,這時候還是反覆讀讀題,找一找規律
  • T2 思路很簡單但還是有些細節要處理的,比如我的85分
  • 這年的題不是很“正宗”,所以D2可能會咕掉,而轉去做其他年份的題