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;
}