看到难度普遍7-9就感觉很劲…再加上doc出品,于是果断入坑。由于知识性内容还没有学完,只能慢慢刷了。
章节 1. 一些简单的练手题
1.1 捕风捉影
这sb题居然智障WA了两次….
由于是回文串只要枚举前半段,然后瞎几把测试素数就好了。
顺便学习一波miller-rabin的二次探查。
复杂度
#include <bits/stdc++.h>
using namespace std;
long long power(long long a, long long n, long long mod)
{
if (n == 0) return 1%mod;
long long p = power(a, n>>1, mod);
p = p*p%mod;
if (n&1) p = p*a%mod;
return p;
}
bool miller_rabin(long long n)
{
if (n == 2) return 1;
if (n < 2 || !(n&1)) return 0;
int k;
long long u;
for (k=0, u=n-1; !(u&1); u>>=1, k++);
for (int i = 1; i < 20; i++) {
long long x = rand()%(n-2)+2, pre;
if (x%n == 0) continue;
x = power(x, u, n);
pre = x;
for (int j = 1; j <= k; j++) {
x = x*x%n;
if (x == 1 && pre != 1 && pre != n-1) return 0;
pre = x;
}
if (x != 1) return 0;
}
return 1;
}
long long rev(long long a)
{
long long t = 0;
while (a) t = (t+a%10)*10, a /= 10;
return t/10;
}
long long ans[100005];
int top = 0;
int main()
{
srand(time(0));
long long n, m;
cin >> n >> m;
if (5 >= n && 5 <= m) ans[++top] = 5;
if (7 >= n && 7 <= m) ans[++top] = 7;
int len = 1;
for (long long i = 1; i <= 10000; i++) {
long long l = i, r = rev(i);
long long lk = l*power(10, len, LONG_LONG_MAX)+r;
if (lk >= n && lk <= m && miller_rabin(lk)) ans[++top] = lk;
for (int j = 0; j < 10; j++) {
lk = l*power(10, len+1, LONG_LONG_MAX)+j*power(10, len, LONG_LONG_MAX)+r;
if (lk >= n && lk <= m && miller_rabin(lk)) ans[++top] = lk;
}
if (i >= power(10, len, LONG_LONG_MAX)-1) len++;
}
sort(ans+1, ans+top+1);
top = unique(ans+1, ans+top+1)-ans-1;
for (int i = 1; i <= top; i++) printf("%lld
", ans[i]);
return 0;
}
1.2 小白逛公园
以前写线段树查询从来都是直接返回答案…做了这题才发现可以返回一个完整的节点信息。
就是线段树维护总和,最大子段和,左起最大子段和,右起最大子段和。
复杂度
#include <bits/stdc++.h>
using namespace std;
const int MAXN = (1<<19)+2;
struct node {
int lsum, rsum, sum, subsum, l, r;
node():l(0),r(0){}
friend bool operator == (const node &a, const node &b)
{
return a.l == b.l;
}
} tree[MAXN*2];
int a[MAXN], n, m;
#define nil tree[0]
node link(const node &l, const node &r)
{
if (l == nil) return r;
if (r == nil) return l;
node nd;
nd.sum = l.sum + r.sum;
nd.lsum = max(l.lsum, l.sum+r.lsum);
nd.rsum = max(r.rsum, r.sum+l.rsum);
nd.subsum = max(l.subsum, max(r.subsum, l.rsum+r.lsum));
return nd;
}
void build(int nd, int l, int r)
{
if (l < r) {
build(nd*2, l, (l+r)/2);
build(nd*2+1, (l+r)/2+1, r);
tree[nd] = link(tree[nd*2], tree[nd*2+1]);
tree[nd].l = l, tree[nd].r = r;
} else {
tree[nd].l = tree[nd].r = l;
tree[nd].sum = tree[nd].lsum = tree[nd].rsum = tree[nd].subsum = a[l];
}
}
void dfs(int nd, int tab = 0)
{
if (!tree[nd].l) return;
for (int i = 1; i <= tab; i++) putchar(' ');
//printf("[%d,%d] : %d,%d,%d,%d
", tree[nd].l, tree[nd].r, tree[nd].lsum, tree[nd].rsum, tree[nd].subsum, tree[nd].sum);
dfs(nd*2, tab+2);
dfs(nd*2+1, tab+2);
}
void update(int nd, int pos, int dat)
{
if (tree[nd].l == tree[nd].r) {
tree[nd].sum = tree[nd].lsum = tree[nd].rsum = tree[nd].subsum = dat;
} else {
int l = tree[nd].l, r = tree[nd].r;
update((pos<=(l+r)/2)?nd*2:nd*2+1, pos, dat);
tree[nd] = link(tree[nd*2], tree[nd*2+1]);
tree[nd].l = l, tree[nd].r = r;
}
}
node query(int nd, int l, int r)
{
if (l > r) return nil;
if (tree[nd].l == l && tree[nd].r == r) return tree[nd];
node d = link(query(nd*2, l, min(r, tree[nd*2].r)),
query(nd*2+1, max(l, tree[nd*2+1].l), r));
//printf("[%d,%d] %d %d->%d,%d,%d
", tree[nd].l, tree[nd].r, l, r, d.subsum, d.lsum,d .rsum);
return d;
}
int main()
{
scanf("%d%d", &n, &m);
tree[0].l = 123231131;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int opt, opl, opr;
scanf("%d%d%d", &opt, &opl, &opr);
if (opt == 1) {
if (opl > opr) swap(opl, opr);
printf("%d
",query(1, opl, opr).subsum);
} else if (opt == 2) {
update(1, opl, opr);
} else dfs(1);
}
return 0;
}
1.3 CoVH之柯南开锁
一开始以为是反转思考了好久,后来才发现只是按下去。
最小割经典建图:
S→row col→T row→col,if there is something at (row,col)
正确性是显然的,复杂度
貌似用二分图匹配也可以?但我不会匈牙利那一套理论。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005, MAXM = 5000005;
struct node {
int to, next, f, neg;
} edge[MAXM];
int head[MAXN], top = 0;
void push(int i, int j, int k)
{
++top, edge[top] = (node) {j, head[i], k, top+1}, head[i] = top;
++top, edge[top] = (node) {i, head[j], 0, top-1}, head[j] = top;
}
int vis[MAXN], bfstime = 0;
int lev[MAXN], S = 50001, T = 50002;
queue<int> que;
bool bfs()
{
vis[S] = ++bfstime, lev[S] = 0, que.push(S);
while (!que.empty()) {
int tp = que.front(); que.pop();
for (int i = head[tp]; i; i = edge[i].next) {
if (edge[i].f == 0 || vis[edge[i].to] == bfstime) continue;
vis[edge[i].to] = bfstime, lev[edge[i].to] = lev[tp]+1, que.push(edge[i].to);
}
}
return vis[T] == bfstime;
}
int dfs(int nd, int maxf = 233333333)
{
if (nd == T || !maxf) return maxf;
int ans = 0, t;
for (int i = head[nd]; i; i = edge[i].next) {
if (edge[i].f == 0 || lev[edge[i].to] != lev[nd] + 1) continue;
t = dfs(edge[i].to, min(maxf, edge[i].f));
ans += t, maxf -= t;
edge[i].f -= t, edge[edge[i].neg].f += t;
}
if (maxf) lev[nd] = -1;
return ans;
}
int dinic()
{
int ans = 0;
while (bfs()) ans += dfs(S);
return ans;
}
int n, m;
char str[105];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) push(S, i, 1);
for (int i = 1; i <= m; i++) push(n+i, T, 1);
for (int i = 1; i <= n; i++) {
scanf("%s", str+1);
for (int j = 1; j <= m; j++)
if (str[j] == '1')
push(i, n+j, 1);
}
cout << dinic() << endl;
return 0;
}
章节 2. 轻松点它们很简单
2.1 cpc的逃离
变得难起来了…
构造问题,由于有二叉树计数考虑用卡特兰数。然后瞎几把搞【雾】。
复杂度
#include <bits/stdc++.h>
using namespace std;
// C : h(n)=h(n-1)*(4*n-2)/(n+1);
long long h[20];
void work(int n, long long k, bool out_side = 1) // n个节点中编号为k的
{
//cerr << endl << n << " " << k << endl;
if (k*n == 0) return;
if (!out_side) putchar('(');
for (int i = 0; i < n; i++) {
if (h[i]*h[n-i-1] < k) k -= h[i]*h[n-i-1];
else {
work(i, (k-1)/h[n-i-1]+1, 0);
putchar('X');
work(n-i-1, k%h[n-i-1]?k%h[n-i-1]:h[n-i-1], 0);
break;
}
}
if (!out_side) putchar(')');
}
int main()
{
h[0] = 1;
for (int i = 1; i < 20; i++) h[i] = h[i-1]*(4*i-2)/(i+1);//, printf("%lld
", h[i]);
long long x;
while (scanf("%lld", &x), x != 0) {
int i = 1;
for (; x > h[i]; x -= h[i], i++);
work(i, x);
puts("");
}
return 0;
}