CSP-S模拟4
考大原题不会做!?我好无语啊……好像还是我为数不多的对着文字版题解自己改的那个(所以才只有70分),好在记住了个m=0,但是上次都能写对的暴力分给写错了***
既然他想帮他引流,我也帮个忙好了-> 题解
中秋节过去了,满月正在走向缺月,我们也正在走向NOIP……
忽然想到了虎哥擦掉4机房黑板的场景,当“hzoi2020 forever”因为hzoi 2021的到来而烟消云散,那种画面竟有着震撼人心的力量——有一天我们也会嘴上说着永远而后天各一方,有一天我们会无奈的承认青春它就是会散场,哪有什么岁月漫长,有的可能只是光阴荏苒物是人非你十年饮冰空自热血难凉……
为什么要卷啊?为什么不卷啊!只是想把我一生中最珍贵最美好的时光,放在我最喜爱最留恋的地方,哪怕没有希望,也要相信希望;如果没有太阳,我就要成为微光!
A. 石子游戏
对于一个连暴力 TLE 50 的做法都看不懂的题,大概只能咕了……
B. 大鱼吃小鱼
当你发现你想用set找前驱写了个set却怎么都过不了编译的时候……于是就搞了一棵带旋转的Treap,至少调了俩小时……啊我要是再忘了set的用法我就***
虽然正解是线段树,但是当我听说平衡树能对并且更好理解的时候,我就去学习了一下无旋Treap,不过到最后也就抄了个 MLE 74 不知道为啥……
upd:后来我又copy了一下Chen_jr的代码然后就A了*
直接引用Delov的博客:
贪心吃当且能吃的最大的显然是对的,假设一条一条的吃,吃到某个时候,我们能吃更大的了,这时候我们一定会去吃这个更大的,于是我们可以考虑模拟这个过程。暴力的话就是拿个map维护所有鱼,然后按从小到大枚举,同时维护一个当且能吃的鱼的集合,我们每次考虑从能吃的里吃掉一些,使得我们能够扩大能吃的鱼的集合,或者先一步达到要求。暴力就是不断选最大的吃,但是显然每次我们吃的都是一段后缀,于是我们可以二分找到这一段一次性吃掉,然后我们就可以吃更大的鱼了。考虑当前体积为now,比now大的最小的鱼体积为x,我们可以一只log的复杂度达到大于x的体积,这时候我们可以吃x了,由于x>now,于是我们的体积翻倍,显然最多只会吃log次,复杂度两只log。
关于实现的话,线段树离散化之类的很难写,考虑用FHQ_Treap,每次把要吃掉的Split出来,剩下的合并然后递归处理,回溯的时候在按原来Split的界Split开,把吃掉的merge回去,就能复原了。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5 + 4;
const int N = 505;
const ll INF = 1e18 + 1e5;
int n, q;
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct FHQ_Treap
{
#define ls t[x].l
#define rs t[x].r
struct node
{
ll val, sum;
int l, r, size, key;
}t[maxn];
int root, tot;
int New(ll val)
{
t[++tot].key = rand();
t[tot].val = t[tot].sum = val;
t[tot].size = 1;
return tot;
}
void pushup(int x)
{
t[x].sum = t[ls].sum + t[rs].sum + t[x].val;
t[x].size = t[ls].size + t[rs].size + 1;
}
void split(int x, int &l, int &r, ll val)
{
if(!x) return l = r = 0, void();
if(val < t[x].val) {split(t[x].l, l, t[x].l, val); r = x;}
else {split(t[x].r, t[x].r, r, val); l = x;}
pushup(x);
}
void split_sum(int x, int &l, int &r, ll sum)
{
if(!x) return l = r = 0, void();
if(t[rs].sum >= sum) {split_sum(rs, rs, r, sum); l = x; pushup(x); return;}
if(t[rs].sum+t[x].val >= sum) {l = ls; r = x; t[x].l = 0; pushup(x); return;}
split_sum(ls, l, ls, sum-t[rs].sum-t[x].val);
r = x; pushup(x);
}
int merge(int x, int y)
{
if(!x || !y) return x | y;
if(t[x].key < t[y].key) {t[x].r = merge(t[x].r, y); pushup(x); return x;}
else {t[y].l = merge(x, t[y].l); pushup(y); return y;}
}
void insert(ll val)
{
int l = 0, r = 0; split(root, l, r, val);
root = merge(merge(l, New(val)), r);
}
void erase(ll val)
{
int l = 0, m = 0, r = 0; split(root, l, r, val); split(l, l, m, val-1);
m = merge(t[m].l, t[m].r);
root = merge(merge(l, m), r);
}
ll findmin(int x)
{
if(x == 0) return INF;
while(t[x].l) {x = t[x].l;}
return t[x].val;
}
int solve(ll s, ll k)
{
if(s > k) return 0;
int l = 0, m = 0, r = 0;
split(root, l, r, s-1);
ll nxt = min(findmin(r), k);
if(s + t[l].sum <= nxt)
{
root = merge(l, r);
return -1;
}
split_sum(l, l, m, nxt-s+1);
root = merge(l, r);
int ans = t[m].size;
int down = solve(s+t[m].sum, k);
l = r = 0;
split(root, l, r, s-1);
root = merge(merge(l,m), r);
if(down == -1) return -1;
return ans + down;
}
}t;
int main()
{
freopen("fish.in", "r", stdin);
freopen("fish.out", "w", stdout);
n = read();
for(int i=1; i<=n; i++)
{
t.insert(read());
}
q = read();
for(int i=1; i<=q; i++)
{
int op = read();
if(op == 1)
{
ll s = read(), k = read();
printf("%d\n", t.solve(s, k-1));
}
if(op == 2) t.insert(read());
if(op == 3) t.erase(read());
}
return 0;
}
C. 黑客
好像不是第一次遇到这种 换个角度枚举就是正解 的题,比如结果的情况比产生结果的方式枚举的次数小很多。
求那个范围时直接除法就好了,我一开始还打算二分***还没写对***
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 3;
const ll mod = 1e9 + 7;
const int INF = 0x7fffffff;
ll A, B, C, D, ans;
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
inline void check(ll a, ll b)
{
ll k1 = ceil((double)A/a), k2 = (double)B/a;
ll k3 = ceil((double)C/b), k4 = (double)D/b;
k1 = max(k1, k3); k2 = min(k2, k4);
if(k1 > k2) return;
ans = (ans + (k2-k1+1)*(a+b)%mod) % mod;
}
inline ll gcd(ll a, ll b)
{
while(b^=a^=b^=a%=b);
return a;
}
int main()
{
freopen("hacker.in", "r", stdin);
freopen("hacker.out", "w", stdout);
A = read(); B = read(); C = read(); D = read();
for(int i=1; i<=999; i++)
{
for(int j=1; j<=999-i; j++)
{
if(gcd(i, j) != 1ll) continue;
check(i, j);
/*ll k1 = ceill((double)A/i), k2 = B/i;
ll k3 = ceill((double)C/j), k4 = D/j;
k1 = max(k1, k3); k2 = min(k2, k4);
if(k1 > k2) continue;
ans += (ll)(i+j)*(k2-k1+1);
ans %= mod;*/
}
}
printf("%lld\n", ans);
return 0;
}
D. 黑客-续
大原题说的就是它!!!NOIP多校联考9 数串
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ull base = 1e18;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct big
{
ull a[60];
big() {memset(a, 0, sizeof(a));}
friend big operator + (const big &a, const big &b)
{
if(a.a[0] == 0) return b;
if(b.a[0] == 0) return a;
big c; c.a[0] = max(a.a[0], b.a[0]);
for(register int i=1; i<=c.a[0]; i++)
{
c.a[i] += a.a[i] + b.a[i];
}
for(register int i=1; i<=c.a[0]; i++)
{
c.a[i+1] += c.a[i] / base;
c.a[i] = c.a[i] % base;
}
if(c.a[c.a[0]+1]) c.a[0]++;
return c;
}
big operator *= (int b)
{
for(register int i=1; i<=a[0]; i++) a[i] = a[i] * b;
for(register int i=1; i<=a[0]; i++)
{
a[i+1] += a[i] / base;
a[i] %= base;
}
if(a[a[0]+1]) a[0]++;
while(a[a[0]] == 0 && a[0] > 1) a[0]--;
return *this;
}
big operator += (const big b)
{
a[0] = max(a[0], b.a[0]);
for(register int i=1; i<=a[0]; i++)
{
a[i] = a[i] + b.a[i];
}
for(register int i=1; i<=a[0]; i++)
{
a[i+1] += a[i] / base;
a[i] %= base;
}
if(a[a[0]+1]) a[0]++;
return *this;
}
void print()
{
printf("%llu", a[a[0]]);
if(a[0])
{
for(register int i=a[0]-1; i; i--)
{
printf("%018llu", a[i]);
}
}
printf("\n");
}
void clear()
{
memset(a, 0, sizeof(a));
}
};
int r[15], n, m, k, Max;
big ans1, ans2;
big cnt[2][1029], sum[2][1029], lj;
int main()
{
freopen("hacker2.in", "r", stdin);
freopen("hacker2.out", "w", stdout);
n = read(); m = read(); k = read();
for(register int i=1; i<=m; i++)
{
int a = read(), b = read();
r[b] = r[b] | (1<<(a-1));
}
Max = (1 << k);
for(register int i=1; i<=k; i++)
{
int zt = (1<<(i-1));
cnt[1][zt].a[1] = 1; cnt[1][zt].a[0] = 1;
sum[1][zt].a[1] = i; sum[1][zt].a[0] = 1;
}
for(register int pos=1; pos<n; pos++)
{
for(register int zt=0; zt<Max; zt++)
{
int now = pos & 1;
if(cnt[now][zt].a[0])
{
sum[now][zt] *= 10;
lj.clear();
for(register int j=1; j<=k; j++)
{
lj += cnt[now][zt];
if(!(r[j] & zt))
{
int nzt = zt | (1<<(j-1));
cnt[1-now][nzt] += cnt[now][zt];
sum[1-now][nzt] += sum[now][zt] + lj;
}
}
cnt[now][zt].clear();
sum[now][zt].clear();
}
}
}
int an = n & 1;
for(register int i=0; i<Max; i++)
{
ans1 += cnt[an][i];
ans2 += sum[an][i];
}
ans1.print(); ans2.print();
return 0;
}