牛客IOI周赛23-普及组
小L的作文
https://ac.nowcoder.com/acm/contest/11164/A
A、小L的作文
给出字符串,求解字符的出现次数。签到题,遍历计数即可。
const int N = 1e5 + 7; ll n, m; char s[5005]; void solve() { char ch = getchar(); getchar(); scanf("%s", s); int ans = 0; for (int i = 0; s[i]; ++i) if (s[i] == ch) ++ans; printf("%d\n", ans); }
B、小L的多项式
因为,所以支持的解法,那么每次输入,直接带入求解即可,对于使用快速模幂即可求解。
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) #define all(__vv__) (__vv__).begin(), (__vv__).end() #define endl "\n" #define pai pair<int, int> #define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__)) #define rep(i, sta, en) for(int i=sta; i<=en; ++i) #define repp(i, sta, en) for(int i=sta; i>=en; --i) typedef long long ll; typedef unsigned long long ull; typedef long double ld; inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; } inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); } inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; } ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; } const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} }; const int MOD = 998244353; const int INF = 0x3f3f3f3f; struct Node { ll val; int id; bool operator < (const Node& opt) const { return val < opt.val; } }; const int N = 1000 + 7; ll n, m; ll p[N]; int calc(int x) { ll ans = 0; rep(i, 0, n) { if (p[i] == 0) continue; ans = (ans + p[i] * qpow(x, i, MOD)) % MOD; } return ans; } void solve() { n = read(); rep(i, 0, n) p[i] = read(); m = read(); while (m--) { int x = read(); int ans = calc(x); print(ans, 32); } } int main() { //int T = read(); while (T--) solve(); return 0; }
C、小L的编辑器
对于给出的字符串第一行叫做,第二行叫做的话。
很显然,我们每次输入完一个指令后,当前字符的相对位置已经确定了。
举个例子
如果当前输入的是a,并且指令是L 那么显然留下的字符串是 |a 不管你后续如何操作a后面的序列都是固定的。 那么同理,输入b,指令为R 留下字符串就是 b| b前面的字符串也是固定的
那么就可以看出来,对于每次的指令,直接把输出出去即可,对于的指令我们要保存在栈里面倒序的输出出来。
这里有一个比赛交了一发掉才知道的点,在中类里面,复杂度是添加。
但是如果写法是,复杂度就是添加,导致了一发,所以才使用栈输出答案。
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) #define all(__vv__) (__vv__).begin(), (__vv__).end() #define endl "\n" #define pai pair<int, int> #define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__)) #define rep(i, sta, en) for(int i=sta; i<=en; ++i) #define repp(i, sta, en) for(int i=sta; i>=en; --i) typedef long long ll; typedef unsigned long long ull; typedef long double ld; inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; } inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); } inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; } ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; } const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} }; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; struct Node { ll val; int id; bool operator < (const Node& opt) const { return val < opt.val; } }; const int N = 1e6 + 7; int n, m; string s, p; char v[N]; void solve() { js; cin >> s >> p; bool op = 0; n = s.size(); int cnt = 0; rep(i, 0, n - 1) { if (p[i] == 'L') { v[++cnt] = s[i]; } else { cout << s[i]; } } repp(i, cnt, 1) cout << v[i]; } int main() { //int T = read(); while (T--) solve(); return 0; }
D、小L的数列
给出个数的序列,你可以从中挑选一些数,并且把这些数随意排序。只需要保证最后你得到的数列,满足下面的要求。
特别的如果序列长度为的话,它本身就是满足要求的。
那么我们抓住关键点,每两个数之间前面的要严格小于后面的数,并且相邻的数之间一定要存在公因子。
我们可以首先写个朴素的算法,时间复杂度,可以写到分。
我们首先知道,对于来说,这个肯定是不存在解的,也就是我们最终求解的序列里面如果可以没有的话,那就一定不会选。
而且还要满足条件,那么相同的数也是无解的。
我们预处理输入的数组,可以得到离散之后的新数组。对于新数组,我们每次枚举一个,再去遍历前面全部的,很容易发现状态转移就是
const int N = 1e5 + 7; int n, m, x, a[N], f[N]; void solve() { n = read(); m = 0; rep(i, 1, n) { x = read(); if (x != 1) a[++m] = x; } sort(a + 1, a + 1 + m); m = unique(a + 1, a + 1 + m) - a - 1; f[1] = 1; int ans = 0; rep(i, 2, m) { f[i] = 1; for (ll j = 1; j < i; ++j) { if (gcd(a[i],a[j]) != 1) f[i] = max(f[i], f[j] + 1); } ans = max(ans, f[i]); } print(ans); }
接下来考虑分的解法,我的做法是建图再去跑动态规划。
我们知道我们上面的方程最花费时间的就是枚举前面出现过的全部,非常的耗费时间,我们又知道。
对于每个来说,它出现过的全部素因子我们是可以分解出来的。我就想到使用素因子做为有向边,把不同的进行连接。再去跑记忆化的一张图,求解出现过的最深节点即可。
首先使用线性筛法筛选中全部的素数。再去分解离散之后的新数组中每个,使用保存存在素因子的下标。
接下来把相邻下标进行有向图的连边,去跑图的遍历,注意这里需要加上记忆化,并且使用去转移求解,如果不上记忆化,这种图直接就会掉。
const int N = 1e5 + 7; int n, m, x, a[N], ans; bool vis[321]; int cnt, prime[321]; vector<int> v[N]; int head[N], tot = 0;//前向星变量 struct Node { //int u; //起点 //int w; //权值 int v, next; } edge[N << 2]; void add(int u, int v) { tot++; //edge[tot].u = u; edge[tot].v = v; //edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot; } int f[N]; int dfs(int u) { if (f[u]) return f[u]; f[u] = 1; for (int i = head[u]; i; i = edge[i].next) f[u] = max(f[u], dfs(edge[i].v) + 1); ans = max(ans, f[u]); return f[u]; } void solve() { ms(f, 0); ms(head, 0); tot = 0; rep(i, 1, N - 1) v[i].clear(); n = read(); ans = m = 0; rep(i, 1, n) { x = read(); if (x != 1) a[++m] = x; } if (!m) { puts("1"); return; } sort(a + 1, a + 1 + m); m = unique(a + 1, a + 1 + m) - a - 1; rep(i, 1, m) { x = a[i]; rep(j, 1, cnt) { if (prime[j] > x) break; if (x % prime[j] == 0) { v[prime[j]].push_back(i); while (x % prime[j] == 0) x /= prime[j]; } } if(x != 1) v[x].push_back(i); } rep(i, 1, a[m]) { n = v[i].size(); rep(j, 1, n - 1) add(v[i][j - 1], v[i][j]); } ans = 1; rep(i, 1, m) if (!f[i]) dfs(i); print(ans); } int main() { rep(i, 2, 320) { if (!vis[i]) prime[++cnt] = i; rep(j, 1, cnt) { if (i * prime[j] >= 320) break; vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } int T = read(); while (T--) solve(); return 0; }