题意:在长度为n的数组(a[i]<=m)内,包括多少个区间[l,r]满足区间中包含1-m所有数
题解:双指针
code:
- #include
- using namespace std;
- typedef long long ll;
- const int N = 1e5 + 20;
- int n, m, a[N], cnt[N], sum;
- ll ans;
-
- int main(){
- scanf("%d%d", &n, &m);
- for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
- for(int l = 1, r= 1; l <= n; l++){
- while(r <= n && sum < m){
- int res = a[r];
- if(!cnt[res]) sum++;
- cnt[res] ++;
- r++;
- }
- if(sum == m)
- ans += n - r + 2;
- cnt[a[l]]--;
- if(!cnt[a[l]]) sum--;
- }
- printf("%lld\n", ans);
- return 0;
- }
题意:有n块荷叶排成一排,从第i个荷叶起跳可以跳到[i + 1, i + a[i]]块荷叶(随机等概率),存在两只青蛙初始都在第一块荷叶,求两只青蛙以相同步数到达第n块荷叶的概率。
题解:
f[i][j]:跳跃i步到达j的概率
方程:![f[i][j + res] += f[i - 1][j] * \frac{1}{res}](https://1000bd.com/contentImg/2023/11/04/194013727.png)
PS:如果正着推时间复杂度为
,考虑处于当前状态对后面状态的影响(区间),可以用前缀和处理
code:
- #include
- using namespace std;
- typedef long long ll;
- const int N = 8010;
- const int mod = 998244353;
- int n, a[N];
- ll f[N][N], b[N];
-
- ll ksm(ll a, ll b){
- ll res = 1;
- while(b){
- if(b & 1) res = res * a % mod;
- a = a * a % mod;
- b >>= 1;
- }
- return res;
- }
-
- int main(){
- scanf("%d", &n);
- for(int i = 1; i <= n; i++){
- scanf("%d", &a[i]);
- b[i] = ksm(a[i], mod - 2);
- }
- f[0][1] = 1;
- for(int i = 1; i < n; i++){ //跳跃i步
- for(int j = 1; j <= n; j++){ // 到达位置j
- int res = a[j];
- f[i][j + 1] = (f[i][j + 1] + f[i - 1][j] * b[j] + mod) % mod;
- f[i][j + res + 1] = (f[i][j + res + 1] - f[i - 1][j] * b[j] + mod) % mod;
- }
- for(int j = 1; j <= n; j++){
- f[i][j] = (f[i][j] + f[i][j - 1] + mod) % mod;
- }
- }
- ll ans = 0;
- for(int i = 1; i < n; i++){
- ans = (ans + f[i][n] * f[i][n] % mod + mod) % mod;
- }
- printf("%lld\n", ans);
- return 0;
- }
题意:构造一个长度小于100的序列,使得序列中最长上升子序列恰好有m个
题解:考虑两个数两个数为一组(例如: 2 1 4 3 6 5...),假设有x组,就会形成
个最长上升子序列,然后考虑在组与组之间插入数【将m拆成二进制】
PS:插入的数也要发挥他的作用
code:
- #include
- using namespace std;
- const int N = 110;
- int t, m;
- struct NODE{
- int id, x;
- }node[N];
-
- bool cmp(NODE a, NODE b){
- return a.x < b.x;
- }
-
- int main(){
- scanf("%d", &t);
- while(t--){
- scanf("%d", &m);
- if(m == 1){
- printf("1\n1\n");
- continue;
- }
- vector<int> ans;
- bitset<32> bit(m);
- int k = 31, res = 100;
- while(!bit[k]) k--;
- ans.push_back(2 * k - 1);
- ans.push_back(2 * k);
- int ned = 0, cnt = 0;
- while(--k >= 0){
- ned++;
- if(bit[k]){
- for(; cnt < ned; cnt++) ans.push_back(res), res--;
- }
- if(k){
- ans.push_back(2 * k - 1);
- ans.push_back(2 * k);
- }
- }
- reverse(ans.begin(), ans.end());
- int n = ans.size();
- printf("%d\n", n);
- for(int i = 0; i < n; i++){
- node[i].id = i;
- node[i].x = ans[i];
- }
- sort(node, node + n, cmp);
- for(int i = 0; i < n; i++){
- ans[node[i].id] = i + 1;
- }
- for(int i = 0; i < n; i++){
- printf("%d ", ans[i]);
- }
- puts("");
- }
- return 0;
- }
(队友补了等于我补了)
题意:求k个串中的不同的公共回文串
题解:解法有很多
code:【队友打的】
- #include
- #define ll long long
- using namespace std;
-
- const int N = 3e5 + 50;
-
- struct node {
- int len, link, ch[26], id;
- }st[N << 1];
- int sz, last;
-
- void sam_init () { sz = last = 1; }
-
- void sam_extend (int c, int id) {
- int cur = ++ sz, p = last;
- st[cur].len = st[p].len + 1, last = cur;
- st[cur].id = id;
- for ( ; p && ! st[p].ch[c]; p = st[p].link) st[p].ch[c] = cur;
- if (! p) st[cur].link = 1;
- else {
- int q = st[p].ch[c];
- if (st[p].len + 1 == st[q].len) st[cur].link = q;
- else {
- int clone = ++ sz;
- st[clone].len = st[p].len + 1;
- st[clone].link = st[q].link;
- st[clone].id = st[q].id;
- memcpy (st[clone].ch, st[q].ch, sizeof (st[q].ch));
- for ( ; p && st[p].ch[c] == q; p = st[p].link) st[p].ch[c] = clone;
- st[q].link = st[cur].link = clone;
- }
- }
- }
-
- char s[5][N], str[N << 1];
- int a[N << 1], b[N], n, k;
- int g[N << 1], f[N << 1];
- int p[N << 2], lef[N << 1];
-
- void manacher (char* s, int len) {
- for (int i = 1, r = 0, mid = 0; i < len; i ++) {
- if (i <= r) p[i] = min (p[(mid << 1) - i], r - i + 1);
- while (s[i - p[i]] == s[i + p[i]]) ++ p[i];
- if (p[i] + i > r) r = p[i] + i - 1, mid = i;
- }
- }
-
- bool vis[N << 1];
-
- void solve () {
- str[0] = '~', str[1] = '|';
- int len = 1;
- for (int i = 0; i < n; i ++)
- str[++ len] = s[0][i], str[++ len] = '|';
- manacher (str, len);
- // printf("%s\n", str);
- for (int i = 1; i <= n; i ++) lef[i] = i;
- for (int i = 2; i < len; i ++) {
- // printf("i = %d, p = %d\n", i / 2, p[i] - 1);
- lef[(i + p[i]) / 2 - 1] = min (lef[(i + p[i]) / 2 - 1], (i - p[i]) / 2 + 1);
- }
- for (int i = n; i >= 2; i --) lef[i - 1] = min (lef[i - 1], lef[i] + 1);
- // for (int i = 1; i <= n; i ++)
- // printf("lef = %d\n", lef[i]);
- ll ans = 0;
- for (int i = 1; i <= sz; i ++) {
- int x = st[i].id - g[i] + 1, y = st[i].id;
- if (x <= y) {
- if (vis[st[i].id]) continue;
- int nx = lef[st[i].id], ny = st[i].id;
- if (x <= nx && (ny - nx + 1 > st[st[i].link].len)) {
- ans ++;
- vis[st[i].id] = true;
- }
- // printf("len = %d\n", st[st[i].link].len);
- // printf("i = %d, x = %d, y = %d, nx = %d, ny = %d\n", st[i].id, x, y, nx, ny);
- }
-
- }
- printf("%lld\n", ans);
- }
-
- int main () {
- // freopen("in.txt", "r", stdin);
- // freopen("test.txt", "w", stdout);
- scanf ("%d%s", &k, s[0]);
- sam_init ();
- n = strlen (s[0]);
- for (int i = 0; i < n; i ++) sam_extend (s[0][i] - 'a', i + 1);
- for (int i = 1; i <= sz; i ++) b[st[i].len] ++;
- for (int i = 1; i <= n; i ++) b[i] += b[i - 1];
- for (int i = sz; i >= 1; i --) a[b[st[i].len] --] = i;
- for (int i = 1; i <= sz; i ++) g[i] = st[i].len;
-
- for (int j = 1; j < k; j ++) {
- scanf ("%s", s[j]);
- for (int i = 1; i <= sz; i ++) f[i] = 0;
- for (int i = 0, v = 1, len = 0; s[j][i]; i ++) {
- int c = s[j][i] - 'a';
- while (v && ! st[v].ch[c]) v = st[v].link, len = st[v].len;
- if (! v) v = 1, len = 0;
- if (st[v].ch[c]) ++ len, v = st[v].ch[c]; // 匹配成功,那么匹配出去即可。
- f[v] = max (f[v], len); // 更新该结点的长度
- }
- for (int i = sz; i >= 1; i --)
- f[st[a[i]].link] = max (f[st[a[i]].link], f[a[i]]);
- for (int i = 1; i <= sz; i ++) g[i] = min (f[i], g[i]);
- }
- // printf("%d\n", *max_element(g + 1, g + sz + 1));
- // for (int i = 1; i <= sz; i ++) {
- // printf("i = %d, len = %d\n", st[i].id, g[i]);
- // }
- solve ();
- return 0;
- }
题意:一个长的为n的数组分成k段,求每一段的最大值之和最小
题解:dp[i][j] : 前i个数分成j段的最小值
![dp[i][j] = min\left \{ dp[k][j - 1] + max(a[k + 1] ~ a[i]) \right \}](https://1000bd.com/contentImg/2023/11/04/194013774.png)
code:
- #include
- using namespace std;
- const int N = 8010;
- const int INF = 0x3f3f3f3f;
- int n, a[N], dp[N][2];
- struct NODE{
- int x, f, minn;
- };
-
- int main(){
- scanf("%d", &n);
- for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
- for(int i = 1; i <= n; i++){
- dp[i][1] = max(dp[i - 1][1], a[i]);
- }
- printf("%d\n", dp[n][1]);
- for(int j = 2; j <= n; j ++){
- stack
st; - for(int i = 1; i <= n; i++){
- dp[i][j & 1] = 0;
- }
- for(int i = 1; i <= n; i++){
- if(i < j){
- dp[i][j & 1] = dp[i][(j - 1) & 1];
- continue;
- }
- if(st.size() && a[i] >= st.top().x){ // 弹出
- int dpmin = dp[i - 1][(j - 1) & 1];
- while(st.size() && a[i] >= st.top().x){
- NODE res = st.top();
- st.pop();
- dpmin = min(dpmin, res.f);
- }
- dp[i][j & 1] = a[i] + dpmin;
- if(st.size()){
- dp[i][j & 1] = min(st.top().minn, dp[i][j & 1]);
- }
- st.push({a[i], dpmin, dp[i][j & 1]});
- }
- else{
- dp[i][j & 1] = a[i] + dp[i - 1][(j - 1) & 1];
- if(st.size()){
- dp[i][j & 1] = min(st.top().minn, dp[i][j & 1]);
- }
- st.push({a[i], dp[i - 1][(j - 1) & 1], dp[i][j & 1]});
- }
- }
- printf("%d\n", dp[n][j & 1]);
- }
- return 0;
- }