2019牛客暑期多校训练营(第七场)
2019牛客暑期多校训练营(第七场)
A.String
暴力\(dp\)即可。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 205;
int T;
char s[N];
int dp[N], pre[N];
bool check(int x, int y) {
for(int d = 1; d <= y - x; d++) {
for(int i = x; i <= y; i++) {
int j = i + d;
if(j > y) j = j - y + x - 1;
if(s[i] != s[j]) {
if(s[j] < s[i]) return 0;
break ;
}
}
}
return 1;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> T;
while(T--) {
cin >> s + 1;
int n = strlen(s + 1);
for(int i = 1; i <= n; i++) pre[i] = 0, dp[i] = n + 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j < i; j++) {
if(dp[j] + 1 < dp[i] && check(j + 1, i)) {
dp[i] = dp[j] + 1;
pre[i] = j;
}
}
}
vector <int> d;
d.push_back(n);
int p = n;
while(pre[p]) d.push_back(p = pre[p]);
reverse(d.begin(), d.end());
for(int i = 0; i < d.size(); i++) {
int j = (i == 0 ? 0 : d[i - 1]);
for(int k = j + 1; k <= d[i]; k++) {
cout << s[k];
}
if(i != d.size() - 1) cout << ' ';
}
cout << '\n';
}
return 0;
}
B.Irreducible Polynomial
结论题,分情况讨论一下即可。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const ll inf = 1000000000000000010LL;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
int kase, n, a[MAXN];
int main() {
ios::sync_with_stdio(false); cin.tie();
cin >> kase;
while (kase--) {
cin >> n;
for (int i = n; ~i; i--)cin >> a[i];
if (n == 2) {
ll x = (ll)a[1] * a[1] - 4LL * a[2] * a[0];
if (x < 0)cout << "Yes\n";
else cout << "No\n";
}
else if (n <= 1)cout << "Yes\n";
else cout << "No\n";
}
}
C.Governing sand
枚举最终最大高度,然后贪心砍掉费用最小的树即可。
首先会砍掉高度大于枚举值的树,之后根据数量在权值线段树中贪心找到答案。
注意不同种类的树的高度可能相同。
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n;
struct node{
int h, c;
ll p, sum;
bool operator < (const node &A)const {
return h < A.h;
}
}a[N], b[N];
ll sum[N];
ll sz[200 << 2], sumv[200 << 2];
void build(int o, int l, int r) {
sz[o] = sumv[o] = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1|1, mid + 1, r);
}
void update(int o, int l, int r, int p, int k) {
sz[o] += k; sumv[o] += 1ll * p * k;
if(l == r) return ;
int mid = (l + r) >> 1;
if(p <= mid) update(o << 1, l, mid, p, k);
else update(o << 1|1, mid + 1, r, p, k);
}
ll query(int o, int l, int r, ll k) {
if(l == r) return sumv[o] / sz[o] * k;
int mid = (l + r) >> 1;
if(sz[o << 1] >= k) return query(o << 1, l, mid, k);
return sumv[o << 1] + query(o << 1|1, mid + 1, r, k - sz[o << 1]);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
while(cin >> n) {
for(int i = 1; i <= n; i++) {
int h, c, p; cin >> h >> c >> p;
a[i] = {h, c, p, 1ll * c * p};
}
sort(a + 1, a + n + 1);
int tot = 0;
for(int i = 1; i <= n; i++) {
if(a[i].h != a[i - 1].h) b[++tot] = a[i];
else b[tot].sum += a[i].sum, b[tot].p += a[i].p;
}
sum[tot + 1] = 0;
for(int i = tot; i >= 1; i--) sum[i] = sum[i + 1] + b[i].sum;
build(1, 1, 200);
int j = 1;
ll ans = INF, cur = 0;
for(int i = 1; i <= tot; i++) {
ll tmp = sum[i + 1];
ll p = b[i].p;
if(p > cur) {
ans = min(ans, tmp);
} else {
ans = min(ans, tmp + query(1, 1, 200, cur - p + 1));
}
cur += b[i].p;
while(j <= n && a[j].h == b[i].h) {
update(1, 1, 200, a[j].c, a[j].p);
j++;
}
}
cout << ans << '\n';
}
return 0;
}
D.Number
签到题。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const ll inf = 1000000000000000010LL;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
int n, p;
int get(int x) {
int ans = 0;
while (x) {
x /= 10;
ans++;
}
return ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie();
cin >> n >> p;
int m = get(p);
if (n < m)cout << "T_T\n";
else {
cout << p;
for (int i = 1; i <= n - m; i++)cout << "0";
cout << '\n';
}
return 0;
}
E.Find the median
题目中生成的区间可能会很大,考虑离散化,但是这样就会影响原来区间的信息。
注意到有用的信息就是区间端点+覆盖次数。那么我们用线段树维护这些信息就行了。线段树中每个结点都维护的一个左闭右开的区间,另外有一些附加的信息,同时还保存每个叶子结点原来的大小(因为是离散化过后的)。
然后就解决了。详见代码吧:
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 400005;
ll a1, b1, c1, m1;
ll a2, b2, c2, m2;
int x[N], y[N], b[N << 1];
int n, D;
void Hash() {
for(int i = 1; i <= n; i++) {
x[i]++; y[i]++;
if(x[i] > y[i]) swap(x[i], y[i]);
b[++D] = x[i], b[++D] = y[i] + 1;
}
sort(b + 1, b + D + 1);
D = unique(b + 1, b + D + 1) - b - 1;
}
ll sumv[N << 3], cntv[N << 3], lazy[N << 3];
void build(int o, int l, int r) {
sumv[o] = cntv[o] = lazy[o] = 0;
if(l == r) return ;
int mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1|1, mid + 1, r);
}
void push_down(int o, int l, int r) {
if(lazy[o]) {
int mid = (l + r) >> 1;
lazy[o << 1] += lazy[o];
cntv[o << 1] += lazy[o];
sumv[o << 1] += lazy[o] * (b[mid + 1] - b[l]);
lazy[o << 1|1] += lazy[o];
cntv[o << 1|1] += lazy[o];
sumv[o << 1|1] += lazy[o] * (b[r + 1] - b[mid + 1]);
lazy[o] = 0;
}
}
void push_up(int o) {
sumv[o] = sumv[o << 1] + sumv[o << 1|1];
}
void update(int o, int l, int r, int L, int R) {
if(L <= l && r <= R) {
sumv[o] += b[r + 1] - b[l];
cntv[o] += 1;
lazy[o] += 1;
return ;
}
push_down(o, l, r);
int mid = (l + r) >> 1;
if(L <= mid) update(o << 1, l, mid, L, R);
if(R > mid) update(o << 1|1, mid + 1, r, L, R);
push_up(o);
}
int query(int o, int l, int r, ll k) {
if(l == r) return b[l] + ((k + cntv[o] - 1) / cntv[o]) - 1;
push_down(o, l, r);
int mid = (l + r) >> 1;
if(sumv[o << 1] >= k) return query(o << 1, l, mid, k);
else return query(o << 1|1, mid + 1, r, k - sumv[o << 1]);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
cin >> x[1] >> x[2] >> a1 >> b1 >> c1 >> m1;
cin >> y[1] >> y[2] >> a2 >> b2 >> c2 >> m2;
for(int i = 3; i <= n; i++) {
x[i] = (a1 * x[i - 1] % m1 + b1 * x[i - 2] % m1 + c1) % m1;
y[i] = (a2 * y[i - 1] % m2 + b2 * y[i - 2] % m2 + c2) % m2;
}
Hash();
build(1, 1, D);
ll sum = 0;
for(int i = 1; i <= n; i++) {
sum += y[i] - x[i] + 1;
int L = lower_bound(b + 1, b + D + 1, x[i]) - b;
int R = lower_bound(b + 1, b + D + 1, y[i] + 1) - b - 1;
update(1, 1, D, L, R);
cout << query(1, 1, D, (sum + 1) / 2) << '\n';
}
return 0;
}
H.Pair
数位\(dp\),加上限制:一个是数字本身的限制,另一个是是否满足题目条件的限制。
对于\(xor\),要求\(t\leq x\) \(xor\) \(y\),只要二进制高位一位满足,其余就可以随便取了;同理对于\(x\) \(and\) \(y \leq t\),只要一位满足小于,后面的也可以随便取。
注意一下\(x,y\)为\(0\)的情况。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 32;
int T;
ll dp[N][2][2][2][2];
int a, b, c;
int na[N], nb[N], nc[N];
ll dfs(int pos, int o1, int o2, int lim1, int lim2) {
if(pos < 0) return 1;
ll &t = dp[pos][o1][o2][lim1][lim2];
if(t != -1) return t;
int up1 = (o1 ? na[pos] : 1);
int up2 = (o2 ? nb[pos] : 1);
ll res = 0;
for(int i = 0; i <= up1; i++) {
for(int j = 0; j <= up2; j++) {
int p = i & j;
int q = i ^ j;
if(lim1 && q < nc[pos]) continue;
if(lim2 && p > nc[pos]) continue;
res += dfs(pos - 1, o1 && (i == up1), o2 && (j == up2), lim1 && (q == nc[pos]), lim2 && (p == nc[pos]));
}
}
return t = res;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> T;
while(T--) {
memset(dp, -1, sizeof(dp));
cin >> a >> b >> c;
for(int i = 31; i >= 0; i--) {
na[i] = (a >> i & 1) ? 1 : 0;
nb[i] = (b >> i & 1) ? 1 : 0;
nc[i] = (c >> i & 1) ? 1 : 0;
}
ll ans = dfs(31, 1, 1, 1, 1);
ans -= max(0, a - c + 1);
ans -= max(0, b - c + 1);
cout << (1ll * a * b) - ans << '\n';
}
return 0;
}
J.A+B problem
签到题。
K.Function
题意:
求\(\sum_{i=1}^nf(i)\),其中,\(f(i)\)为:
\[\left\{
\begin{aligned}
&3e+1,&i= p^e \ and\ p\%4=1\\
&1,&else
\end{aligned}
\right.
\]
思路:
考虑\(min25\)筛求解。
我们不管1,2的存在,最后加上其贡献即可。
首先求出\(g_1,g_2\),分别质数表示\(\% 4\)为1和3的情况总数,然后一个一个来筛。筛的时候要同时考虑当前质数模4的情况以及对\(g_1,g_2\)的影响,它们都会减去某个值。
然后就直接求和就行了,基本都是套用板子。
最主要的还是求\(g\)的思路。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
int T;
ll n;
ll sum1[N], sum2[N], prime[N];
ll w[N], ind1[N], ind2[N];
ll g1[N], g2[N];
bool chk[N];
int tot, cnt;
void pre(int n) { // \sqrt
chk[1] = 1;
for(int i = 1; i <= n; i++) {
if(!chk[i]) {
prime[++tot] = i;
sum1[tot] = sum1[tot - 1];
sum2[tot] = sum2[tot - 1];
if(i % 4 == 1) ++sum1[tot];
if(i % 4 == 3) ++sum2[tot];
}
for(int j = 1; j <= tot && prime[j] * i <= n; j++) {
chk[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
void calc_g() {
int z = sqrt(n);
for(ll i = 1, j; i <= n; i = j + 1) {
j = n / (n / i);
w[++cnt] = n / i;
g1[cnt] = w[cnt] / 4 + (w[cnt] % 4 >= 1) - 1;
g2[cnt] = w[cnt] / 4 + (w[cnt] % 4 >= 3);
if(n / i <= z) ind1[n / i] = cnt;
else ind2[n / (n / i)] = cnt;
}
for(int i = 1; i <= tot; i++) {
for(int j = 1; j <= cnt && prime[i] * prime[i] <= w[j]; j++) {
ll tmp = w[j] / prime[i], k;
if(tmp <= z) k = ind1[tmp]; else k = ind2[n / tmp];
if(prime[i] % 4 == 1) {
g1[j] -= (g1[k] - sum1[i - 1]);
g2[j] -= (g2[k] - sum2[i - 1]);
} else if(prime[i] % 4 == 3) {
g1[j] -= (g2[k] - sum2[i - 1]);
g2[j] -= (g1[k] - sum1[i - 1]);
}
}
}
}
int f(int p, int q) {
if(p % 4 == 1) return 3 * q + 1;
return 1;
}
ll S(ll x, int y) { // 2~x >= P_y
if(x <= 1 || prime[y] > x) return 0;
ll z = sqrt(n);
ll k = x <= z ? ind1[x] : ind2[n / x];
ll ans = 4 * g1[k] - 4 * sum1[y - 1] + g2[k] - sum2[y - 1];
if(y == 1) ans++;
for(int i = y; i <= tot && prime[i] * prime[i] <= x ; i++) {
ll pe = prime[i], pe2 = prime[i] * prime[i];
for(int e = 1; pe2 <= x; ++e, pe = pe2, pe2 *= prime[i]) {
ans += f(prime[i], e) * S(x / pe, i + 1) + f(prime[i], e + 1);
}
}
return ans;
}
int main() {
freopen("input.in", "r", stdin);
cin >> T;
while(T--) {
memset(chk, 0, sizeof(chk)); tot = cnt = 0;
cin >> n;
int tmp = sqrt(n);
pre(tmp);
calc_g();
cout << S(n, 1) + 1 << '\n';
}
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。