NOIP模拟1

警示后人:注意考试结束时间

11:25 一会开始打暴力

11:30 wc怎么出分了

还好前面的题没挂

话说今天暴力打满有250啊,大家都没怎么打暴力吗?

A. 语言

发现枚举哪里是动词,判断是否合法即可

名词的定义可以转化为最后一个是名词,不存在动词

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 200005;
int n;
int w[50];
char s[maxn];
bool chkN(int i){return w[i] == 2 || w[i] == 3 || w[i] == 6 || w[i] == 7;}
bool sol(){
	for(int i = 1; i <= 26; ++i)scanf("%d",&w[i]);
	scanf("%s",s + 1); n = strlen(s + 1);
	if(chkN(s[n] - 'a' + 1) == false)return false;
	if(w[s[1] - 'a' + 1] == 4)return false;
	int mir;
	for(mir = n; mir >= 1; --mir)if(w[s[mir - 1] - 'a' + 1] == 4)break;
	for(int i = 2; i < n; ++i){
		int now = w[s[i] - 'a' + 1];
		if(now >= 4){
			if(chkN(s[i - 1] - 'a' + 1) && mir <= i + 1)return true;
			if(w[s[i] - 'a' + 1] == 4)break;
		}
	}
	return false;
}
int main(){
	freopen("language.in","r",stdin);
	freopen("language.out","w",stdout);
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)if(sol())printf("Yes\n");else printf("No\n");
	return 0;
}

B. 色球

双向链表,为了省事我直接 \(vector\) 建图

难点在于这题没有大样例,需要手写对拍,不过您如果和 \(Delov\) 一样巨的话就可以写完直接交

好像很多人打平衡树?

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 300005;
struct node{int col, cnt;}d[maxn];
vector<int>g[maxn];
int head[maxn], tail[maxn], tot;
int n, m;
char in[10];
void link(int u, int v){
	g[u].push_back(v); g[v].push_back(u);
}
void cut(int u, int v){
	int s = g[u].size(); for(int i = 0; i < s; ++i)if(g[u][i] == v){swap(g[u][i], g[u][s - 1]);break;}
		s = g[v].size(); for(int i = 0; i < s; ++i)if(g[v][i] == u){swap(g[v][i], g[v][s - 1]);break;}
	g[u].pop_back(); g[v].pop_back();
}
void push(){
	int x = read(), y = read(), z = read();
	d[++tot] = {y, x};
	if(head[z])link(head[z], tot);
	if(!tail[z])tail[z] = tot;
	head[z] = tot;
}
void pop(){
	int x = read(), z = read(), now = head[z];
	while(x){
		int net = 0;
		if(x >= d[now].cnt){
			x -= d[now].cnt;
			if(g[now].size())net = g[now][0];
			else net = 0;
			if(net)cut(now, net);
			head[z] = net;
		}else{
			d[now].cnt -= x;
			x = 0;
		}
		if(x)now = net;
	}
	if(head[z] == 0)tail[z] = 0;
	printf("%d\n",d[now].col);
}
void put(){
	int u = read(), v = read();
	if(!tail[u])return;
	if(head[v] && head[u])link(head[v], head[u]);
	if(!tail[v])tail[v] = head[u];
	head[v] = tail[u];
	tail[u] = head[u] = 0;
}
int main(){
	// freopen("color.in","r",stdin);
	// freopen("color.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		scanf("%s",in + 1);
		if(in[3] == 's')push();
		if(in[3] == 'p')pop();
		if(in[3] == 't')put();
	}
	return 0;
}

C. 斐波

考场上基本没看这题

首先 \(f = fib^2\) 有递推公式 \(f_{n} = 2f_{n - 1} + 2f_{n - 2} - f_{n - 3}\)

\(fib_n^2 = fib_{n - 1}^2 + fib_{n - 2}^2 + 2fib_{n - 1}fib_{n - 2} = 2fib_{n - 1}^2 + 2fib_{n - 2}^2 - fib_{n - 3}\)

那么就可以想到矩阵加速,推出转移矩阵 \(trans\)

考虑对于已知某个集合的答案 \(a\),现在加入一个新元素 \(x\)

那么选择和不选择 \(x\) 会对答案产生两部分贡献,答案变成 \(a * (trans^x + I)\)

于是考虑在线段树上进行维护,每次考虑合并两个区间,对答案新的贡献就是跨越了中点的区间,即左区间的后缀和右区间的前缀,因为矩阵乘法满足结合律,于是可以直接用加法,配合乘法处理出前后缀

突然想到另外一种理解方式,因为 \(T\) 是枚举 \(S\) 的子集,那么就是 \(S\) 的每个元素选或不选,所以一个区间的答案就是 \(\Pi (trans^{a_i} +I)\)

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 100005;
const int mod = 998244353;
struct matrix{
	int a[3][3];
	matrix(){memset(a, 0, sizeof(a));}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix ans;
		for(int i = 0; i < 3; ++i)
			for(int k = 0; k < 3; ++k){
				int res = x.a[i][k];
				for(int j = 0; j < 3; ++j)ans.a[i][j] = (ans.a[i][j] + 1ll * res * y.a[k][j]) % mod;
			}
		return ans;
	}
	friend matrix operator + (const matrix &x, const matrix &y){
		matrix ans = x;
		for(int i = 0; i < 3; ++i)
			for(int j = 0; j < 3; ++j)
				ans.a[i][j] = (ans.a[i][j] + y.a[i][j]) % mod;
		return ans;
	}
}tr[maxn], trans, I, e;
int n, q, a[maxn];
struct node{matrix sum, suf, pre, pi;};
struct seg{
	node t[maxn << 2 | 1];
	node merge(node l, node r){
		node x;
		x.pi = l.pi * r.pi;
		x.pre = l.pre + l.pi * r.pre;
		x.suf = l.suf * r.pi + r.suf;
		x.sum = l.sum + r.sum + l.suf * r.pre; 
		return x;
	}
	void built(int x, int l, int r){
		if(l == r){
			t[x].pi = t[x].pre = t[x].suf = t[x].sum = tr[a[l]] + I;
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		t[x] = merge(t[x << 1], t[x << 1 | 1]);
	}
	void modify(int x, int l, int r, int pos){
		if(l == r){
			t[x].pi = t[x].pre = t[x].suf = t[x].sum = tr[a[l]] + I;
			return;
		}
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos);
		else modify(x << 1 | 1, mid + 1, r, pos);
		t[x] = merge(t[x << 1], t[x << 1 | 1]);
	}
	node query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1;
		if(L > mid) return query(x << 1 | 1, mid + 1, r, L, R);
		if(R <= mid) return query(x << 1, l, mid, L, R);
		return merge(query(x << 1, l, mid, L, R), query(x << 1 | 1, mid + 1, r, L, R));
	}
}t;
int main(){
	freopen("fib.in","r",stdin);
	freopen("fib.out","w",stdout);
	n = read(), q = read();
	trans.a[0][0] = trans.a[1][0] = 2; trans.a[2][0] = -1; 
	trans.a[0][1] = trans.a[1][2] = 1;
	e.a[0][1] = e.a[0][2] = 1;
	for(int i = 0; i < 3; ++i)I.a[i][i] = 1;
	for(int i = 1; i <= n; ++i)a[i] = read();
	tr[0] = I; for(int i = 1; i <= 100000; ++i)tr[i] = tr[i - 1] * trans;
	t.built(1, 1, n);
	for(int i = 1; i <= q; ++i){
		int op = read(), x = read(), y = read();
		if(op & 1) a[x] = y, t.modify(1, 1, n, x);
		else printf("%d\n",((e * t.query(1, 1, n, x, y).sum).a[0][0] % mod + mod) % mod); 
	}
	return 0;
}

D. 偶数

字符串重复两次看起来不是很优雅,于是只看一半

发现找最短的一段,本质上就是找最长的除了整个串以外,相同的前后缀长度。即最大周期

于是可以用 \(kmp\) 拿到 \(40\) 分暴力

但是这个东西你只用来拿暴力分吗?

当然要打表了

把表打出来发现每次变化要么是相同的,要么是类似 \(fib\) 的形式

于是可以处理了

然而考场上还在想怎么处理时就出分了,后两题愉快白板

参考褐了题解相对优雅的写法

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1000005;
const int mod = 998244353;
char s[maxn];
int nxt[maxn], h[maxn], bp[maxn];
int qpow(ll x, ll y){
	y %= (mod - 1);
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
ll m;
int q, n, mx;
ll l[105];
int f[105], rt[105];
int sol(ll x){
	ll sum = 0; int ans = 0;
	for(int i = mx; i >= 0; --i)if(sum + l[i] <= x){
		ans = (ans * 1ll * rt[i] % mod + f[i]) % mod;
		sum += l[i];
	}
	ans = (1ll * ans * qpow(10, x - sum) + h[x - sum]) % mod;
	return ans;
}
void sol(){
	scanf("%s",s + 1);
	n = strlen(s + 1) / 2;
	for(int i = 2, las = 0; i <= n; ++i){
		while(las && s[las + 1] != s[i])las = nxt[las];
		if(s[las + 1] == s[i])++las;
		nxt[i] = las;
	}
	for(int i = 1; i <= n; ++i)h[i] = (10ll * h[i - 1] + s[i] - '0') % mod;
	scanf("%lld%d",&m,&q);
	l[0] = n - nxt[n]; f[0] = h[l[0]]; rt[0] = qpow(10, l[0]);
	l[1] = n; f[1] = h[n]; mx = 1; rt[1] = qpow(10, l[1]);
	for(int i = 2; i <= 100; ++i){
		l[i] = l[i - 1] + l[i - 2]; rt[i] = qpow(10, l[i]);
		f[i] = (1ll * f[i - 1] * rt[i - 2] + f[i - 2]) % mod;
		if(l[i] >= m){mx = i; break;}
	}
	for(int i = 1; i <= q; ++i){
		ll l, r; scanf("%lld%lld",&l,&r);
		int ans = sol(r) - 1ll * sol(l - 1) * qpow(10, r - l + 1) % mod;
		ans = (ans % mod + mod) % mod;
		printf("%d\n",ans);
	}
}
int main(){
	freopen("even.in","r",stdin);
	freopen("even.out","w",stdout);
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

posted @ 2022-11-02 17:46  Chen_jr  阅读(15)  评论(5编辑  收藏  举报