斜着看,每次复制一遍加一个
用一个栈维护,如果当前可以打败栈顶的,就出栈,如果相同,入不入栈都可以,如果打不过也要入栈,因为它可以阻挡后面的。
最后栈底就是留下来的。
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 1e6 + 10;
- char s[N];
- int top;
-
- int main()
- {
- map<char, int> mp;
- mp['S'] = 0; mp['P'] = 1; mp['R'] = 2;
-
- int T; scanf("%d", &T);
- while(T--)
- {
- string str; cin >> str;
- int len = str.size();
-
- top = 0;
- rep(i, 0, len)
- {
- while(top && (mp[str[i]] + 1) % 3 == mp[s[top]]) top--;
- s[++top] = str[i];
- }
- printf("%c\n", s[1]);
- }
-
- return 0;
- }
用根号为分界线,一部分暴力,一部分预处理
对于这道题,模数小于等于根号n时,直接预处理
模数大于等于根号n时,直接暴力
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 1.5e5 + 10;
- const int M = 400;
- int s[M][M], a[N], n, m;
-
- int main()
- {
- scanf("%d%d", &n, &m);
- _for(i, 1, n) scanf("%d", &a[i]);
-
- int block = sqrt(n);
- _for(i, 1, n)
- _for(j, 1, block)
- s[j][i % j] += a[i];
-
- while(m--)
- {
- char op[10]; int x, y;
- scanf("%s%d%d", op, &x, &y);
- if(op[0] == 'A')
- {
- if(x <= block) printf("%d\n", s[x][y]);
- else
- {
- int res = 0;
- for(int i = y; i <= n; i += x) res += a[i];
- printf("%d\n", res);
- }
- }
- else
- {
- _for(j, 1, block) s[j][x % j] += y - a[x];
- a[x] = y;
- }
- }
-
- return 0;
- }
1e5左右的数据范围要很敏感,很可能是n根号n的算法
这题而言,每次p最少会增加k
那么当k大于根号n时,最多跳根号n次
当k小于根号n时,可以用dp直接求出,倒着求一遍
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 1e5 + 10;
- const int M = 350;
- int dp[N][M], a[N], n, m;
-
- int main()
- {
- scanf("%d", &n);
- _for(i, 1, n) scanf("%d", &a[i]);
-
- int block = sqrt(n);
- for(int i = n; i >= 1; i--)
- _for(k, 1, block)
- {
- int j = i + a[i] + k;
- if(j > n) dp[i][k] = 1;
- else dp[i][k] = dp[j][k] + 1;
- }
-
- scanf("%d", &m);
- while(m--)
- {
- int p, k;
- scanf("%d%d", &p, &k);
- if(k <= block) printf("%d\n", dp[p][k]);
- else
- {
- int res = 0;
- while(p <= n)
- {
- p = p + a[p] + k;
- res++;
- }
- printf("%d\n", res);
- }
- }
-
- return 0;
- }
注意要逆推,当前的选择肯定根据选择之后的结果是什么
逆推的话,记忆化搜索比写循环要方便
注意空间要开两倍
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 1e5 + 10;
- int a[N], b[N], n, k;
- ll dp[N << 1][15][15]; //交之前一定要检查一下空间开够了没有
-
- ll dfs(int round, int numa, int numb)
- {
- if(round == 2 * n + 1) return 0;
- if(dp[round][numa][numb] != -1) return dp[round][numa][numb];
-
- int ta = round / 2 - numb + numa;
- int tb = (round + 1) / 2 - numa + numb;
-
- if(round % 2 == 1)
- {
- ll res = dfs(round + 1, numa, numb);
- if(ta + 1 <= n && numa + 1 <= k) res = max(res, dfs(round + 1, numa + 1, numb) + a[ta + 1]);
- return dp[round][numa][numb] = res;
- }
- else
- {
- ll res = dfs(round + 1, numa, numb);
- if(tb + 1 <= n && numb + 1 <= k) res = min(res, dfs(round + 1, numa, numb + 1) - b[tb + 1]);
- return dp[round][numa][numb] = res;
- }
- }
-
- int main()
- {
- scanf("%d%d", &n, &k);
- _for(i, 1, n) scanf("%d", &a[i]);
- _for(i, 1, n) scanf("%d", &b[i]);
-
- sort(a + 1, a + n + 1, greater<int>());
- sort(b + 1, b + n + 1, greater<int>());
-
- memset(dp, -1, sizeof dp);
- printf("%lld\n", dfs(1, 0, 0));
-
- return 0;
- }
很容易想到dp[i][j]表示前i个物品分成j组的方案数
当前物品要不独立成一组,要不和之前的一组
设p为最大的下标使得ai - ap >= r
那么显然ai只能和1到p分为一组
那么关键是1到p有多少组可以让ai加进去呢
注意到p+1到i-1这一部分,两两是不可能为一组的,且ai不能和它们一组
那么只剩下j - (i - p - 1)这么多组了,ai可以加入其中一个
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 5000 + 10;
- const int mod = 998244353;
- int L[N], a[N], n, k, R;
- ll dp[N][N];
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%d%d%d", &n, &k, &R);
- _for(i, 1, n) scanf("%d", &a[i]);
-
- int l = n;
- for(int r = n; r >= 1; r--)
- {
- while(l - 1 >= 0 && a[r] - a[l] < R) l--;
- L[r] = l;
- }
-
- dp[0][0] = 1;
- _for(i, 1, n)
- _for(j, 1, k)
- {
- dp[i][j] = dp[i - 1][j - 1];
- int t = i - L[i] - 1;
- if(j - t > 0) dp[i][j] = (dp[i][j] + dp[i - 1][j] * (j - t) % mod) % mod;
- }
- printf("%lld\n", dp[n][k]);
- }
-
- return 0;
- }
那a,b,c三个数试一下,是abc+ac+bc+ab+a+b+c
每一项就是abc这三个取字母或者取1
可以写成(a + 1)(b + 1)(c + 1) - 1
那么答案就是加1乘起来然后减1
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int mod = 998244353;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- int n; scanf("%d", &n);
-
- ll ans = 1;
- _for(i, 1, n)
- {
- int x; scanf("%d", &x);
- ans = ans * (x + 1) % mod;
- }
- ans--;
- if(ans < 0) ans += mod;
- printf("%lld\n", ans);
- }
-
- return 0;
- }
找和u和v互质的数有多少个,那么将两个数质因数分解,然后容斥一下即可
主要要特判gcd=2的情况
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 1e7 + 10;
- int vis[N], a[N], n, q, ans;
- vector<int> p, ve;
-
- void init()
- {
- vis[0] = vis[1] = 1;
- _for(i, 2, 1e7)
- {
- if(!vis[i]) a[i] = i, p.push_back(i); //取最小质因子的时候不要忘了质数为质数本身
- for(int x: p)
- {
- if(x * i > 1e7) break;
- vis[x * i] = 1;
- a[x * i] = x;
- if(i % x == 0) break;
- }
-
- }
- }
-
- vector<int> deal(int x)
- {
- vector<int> res;
- while(x > 1)
- {
- int t = a[x];
- res.push_back(t);
- while(x % t == 0) x /= t;
- }
- return res;
- }
-
- void dfs(int i, int cnt, ll cur)
- {
- if(i == ve.size())
- {
- if(cur > 1)
- {
- if(cnt % 2 == 1) ans += n / cur;
- else ans -= n / cur;
- }
- return;
- }
- dfs(i + 1, cnt + 1, cur * ve[i]);
- dfs(i + 1, cnt, cur);
- }
-
- int gcd(int a, int b) { return !b ? a: gcd(b, a % b); }
-
- int main()
- {
- init();
-
- scanf("%d%d", &n, &q);
- while(q--)
- {
- int u, v;
- scanf("%d%d", &u, &v);
- vector<int> pu = deal(u), pv = deal(v);
-
- ve = pu;
- for(int x: pv) ve.push_back(x);
- sort(ve.begin(), ve.end());
- ve.erase(unique(ve.begin(), ve.end()), ve.end());
-
- if(ve.size() == pu.size() + pv.size())
- {
- puts("1 1");
- continue;
- }
-
- ans = 0;
- dfs(1, 1, ve[0]);
- dfs(1, 0, 1);
- printf("%d %d\n", 2, n - ans + (gcd(u, v) == 2));
- }
-
- return 0;
- }
这题妙啊,奇数偶数是突破口
奇数全部放左边,偶数全部放右边
那么三元组一定全在左边或权在右边
然后考虑递归处理,比如现在全是奇数,那么相减后最低位都为0,那么就没用了,所有数直接右移一位,这时又产生了奇数和偶数。
考虑二进制位,从最低位开始,为1的放左边为0放右边,然后再看倒数第二位……
那么就按照这个策略排序一遍,就一定不会有等差数列
特判一下一个数出现三次以上
- #include<bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 5e3 + 10;
- unordered_map<int, int> mp;
- int a[N], n;
-
- bool cmp(int x, int y)
- {
- _for(j, 0, 30)
- {
- int tx = (x >> j) & 1, ty = (y >> j) & 1;
- if(tx != ty) return tx > ty;
- }
- return true; //注意可能有相等的情况 要return true
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- mp.clear();
- scanf("%d", &n);
-
- int flag = 1;
- _for(i, 1, n)
- {
- scanf("%d", &a[i]);
- if(++mp[a[i]] >= 3) flag = 0;
- }
-
- if(!flag)
- {
- puts("NO");
- continue;
- }
-
- puts("YES");
- sort(a + 1, a + n + 1, cmp);
- _for(i, 1, n) printf("%d ", a[i]);
- puts("");
- }
-
- return 0;
- }