要试题的话,我现在只有Day2的试题 Update:我现在有Day1的试题了
CQOI2016 MLE滚粗。。。
Day0:去邮电报道,把键盘搞坏了,被那里的人骂了。心里诅咒了回去。。
Day1:开始考试了,一看T1不是ZJOI201X最小割吗?赶紧码,骂完了对拍了一会。看T2去了k远点对,做不来啊。去看T3不是SB数位DP吗?赶紧码。码完了对拍了一会,(其实是人脑造数据,写不来暴力,造数据的时候,就是去想4位数,考虑4位数的情况比如像1000到1999有19个还是18个。。)然后就去写T2暴力去了,骗到了30分
Day1完了之后还自我感觉良好,有230呢,在去年都可以进队了(flag,而且今天的题目比去年还难,回家复习了一下PollardRho、打了一发主席树和Splay,然后就睡觉了。
Day2:开始考试了,啥?T1裸的PollardRho?天助我也!然后赶紧敲板,敲完了感觉就A了,然后去钢T2,最开始钢的时候脑子有点晕,然后乱打了一通,发现是错的,然后有点小着急。不过在12点钟的时候钢了出来,放心的去干T3了(flag)。中途拉肚子…然后就狼了30分钟左右。。T3没干出来。
考完试出来其实并不是很虚,毕竟昨天的分在前几年都可以进队了。。然后等成绩,干坐了1h。看到老师来了,说我们有一个人没进队,心里打抖了,结果竟然真的是我。。。心里不住的。。
我不敢信啊,我Day2T2爆零了!玛德,我要骂人啊。一看自己的程序,妈的智障,我内存开大了啊!玛德智障!我是SB啊!就这么轻易的滚粗了吗?我真的无法接受这一切。OI,我毕竟已经爱上了。
原谅我的吐槽吧。
我把我做出来的题写个题解
Day1T1: 给出一个无向图,求大小不同的最小割有多少个。GH-Tree的裸题,可以像ZOJ最小割一样用分治来求解。原因是根据GH-Tree,本质不同的最小割之后O(n)个,所以时间复杂度是O(n*网络流)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXM 300005
#define MAXN 900
#define INF 0x3f3f3f3f3f3f3f3fLL
#define LL long long
struct Node { int v, nxt; LL f; } e[MAXM];
int n, m, adj[MAXN], c = 1, a[MAXN], b[MAXN], S, T, N, d[MAXN], vd[MAXN];
LL f[MAXN][MAXN];
inline void Add(int u, int v, LL f) {
++ c; e[c].v = v; e[c].f = f; e[c].nxt = adj[u]; adj[u] = c;
++ c; e[c].v = u; e[c].f = f; e[c].nxt = adj[v]; adj[v] = c;
}
const LL MOD = 504977;
struct HashMap {
LL key[MAXN]; int adj[MOD], nxt[MAXN], sz;
HashMap() { memset(adj, 0, sizeof adj); sz = 0; }
inline void Insert(LL v) {
int u = v % MOD; key[++ sz] = v;
nxt[sz] = adj[u]; adj[u] = sz;
}
inline bool Find(LL v) {
int u = v % MOD;
for(int i = adj[u]; i; i = nxt[i]) if(key[i] == v) return 1;
return 0;
}
} mp;
LL Aug(int u, LL augco) {
if(u == T) return augco;
int dmin = N-1, v; LL augc = augco, delta;
for(int i = adj[u]; i; i = e[i].nxt) if(e[i].f) {
v = e[i].v;
if(d[u] == d[v] + 1) {
delta = Aug(v, min(augc, e[i].f));
e[i].f -= delta; e[i^1].f += delta;
augc -= delta;
if(!augc || d[S] >= N) return augco - augc;
}
if(d[v] < dmin) dmin = d[v];
}
if(augco == augc) {
-- vd[d[u]];
if(!vd[d[u]]) d[S] = N;
++ vd[d[u] = dmin+1];
}
return augco - augc;
}
LL sap(int s, int t, int n) {
S = s; T = t; N = n; LL ans = 0;
memset(d, 0, sizeof d);
memset(vd, 0, sizeof vd);
vd[0] = N;
while(d[S] < N) ans += Aug(S, INF);
return ans;
}
bool vis[MAXN];
void Restore() {
for(int i = 2; i <= c; i += 2)
e[i].f = e[i^1].f = (e[i].f + e[i^1].f) / 2;
}
void dfs(int u) {
vis[u] = 1;
for(int i = adj[u]; i; i = e[i].nxt)
if(e[i].f > 0 && !vis[e[i].v]) dfs(e[i].v);
}
void DAC(int l, int r) {
if(l == r) return;
Restore();
LL cut = sap(a[l], a[r], n); int lp = l, rp = r;
for(int i = 1; i <= n; ++ i) vis[i] = 0;
dfs(S);
for(int i = 1; i <= n; ++ i) if(vis[i])
for(int j = 1; j <= n; ++ j) if(!vis[j])
f[j][i] = f[i][j] = min(f[i][j], cut);
for(int i = l; i <= r; ++ i)
if(vis[a[i]]) b[lp ++] = a[i];
else b[rp --] = a[i];
for(int i = l; i <= r; ++ i) a[i] = b[i];
DAC(l, rp); DAC(lp, r);
}
inline void GET(int &n) {
static char c; n = 0;
do c = getchar(); while('0' > c || c > '9');
do n=n*10+c-'0',c=getchar(); while('0' <= c && c <= '9');
}
int main() {
freopen("cuts.in", "r", stdin);
freopen("cuts.out","w",stdout);
scanf("%d%d", &n, &m); int u, v, w;
for(int i = 1; i <= m; ++ i) {
GET(u); GET(v); GET(w);
Add(u, v, w);
}
memset(f, 0x3f, sizeof f);
for(int i = 1; i <= n; ++ i) a[i] = i;
DAC(1, n);
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
if(f[i][j] < INF && !mp.Find(f[i][j])) mp.Insert(f[i][j]);
printf("%d\n", mp.sz);
fclose(stdin); fclose(stdout);
return 0;
}
Day1T3:数位DP,我考场上没有经过优化的状态是 f[i][j][k][l][m][n] 表示 i 位数第 i 位是j,第 i−1 位是 k ,是否有4,是否有8,是否已近合法,这样的转移状态数是 O(11∗10∗10∗2∗2∗2) 乘上一个枚举的次数,
所以最坏时间复杂度大约是 O(80000) ,转移易得
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
LL f[12][11][11][2][2][2], L, R;
int bit[20];
LL dp(int i, int j, int k, int l, int m, int n, bool lim) {
if(!l && (j == 4 || k == 4)) return 0;
if(!m &&(j == 8 || k == 8)) return 0;
if(2 == i) {
if(n) return 0; if(l && j != 4 && k != 4) return 0;
if(m && j != 8 && k != 8) return 0;
return 1;
}
if(l && m) return 0; if(n && i < 3) return 0;
if(!lim && ~f[i][j][k][l][m][n]) return f[i][j][k][l][m][n];
int o = lim ? bit[i-2] : 9; LL ans = 0;
if(i >= 3 && n && j == k) {
if((lim && j <= o) || !lim) {
ans += dp(i-1, j, j, l, m, 0, lim && o == j);
ans += dp(i-1, j, j, l, m, 1, lim && o == j);
}
}
for(int q = 0; q <= o; ++ q) {
if(k == q && j == q) continue;
if(q == 4 && !l) continue;
if(q == 8 && !m) continue;
ans += dp(i-1, k, q, l, m, n, lim && q == o);
if(j == 4) ans += dp(i-1, k, q, 0, m, n, lim && q == o);
if(j == 8) ans += dp(i-1, k, q, l, 0, n, lim && q == o);
}
if(!lim) f[i][j][k][l][m][n] = ans;
return ans;
}
LL calc(LL t) {
if(t < 10000000000LL) return 0;
//for(int i = 1; i <= 11; ++ i)
// bit[i] = t % 10, t /= 10;
int cnt = 0;
while(t) bit[++ cnt] = t%10, t/=10;
LL ans = 0;
for(int i = 1; i <= bit[cnt]; ++ i)
for(int j = 0; j <= ((i == bit[cnt]) ? bit[cnt-1] : 9); ++ j)
ans += dp(cnt, i, j, 1, 0, 1, i == bit[cnt] && j == bit[cnt-1]) + dp(cnt, i, j, 0, 1, 1, i == bit[cnt] && j == bit[cnt-1]) + dp(cnt, i, j, 0, 0, 1, i == bit[cnt] && j == bit[cnt-1]);
return ans;
}
int main() {
freopen("number.in", "r", stdin);
freopen("number.out","w",stdout);
memset(f, -1, sizeof f);
cin >> L >> R;
-- L;
cout << calc(R) - calc(L);
fclose(stdin); fclose(stdout);
return 0;
}
Day2T1:我们知道只需要一个很快的素数分解就能解决问题,所以PollardRho,就是你了,其他的按照要求做就行了。(不知道为什么,我现场AC的程序在BZOJ上要RE。。)
#include <cstdio>
#define LL long long
LL e, N, c, d, n, r;
LL Mul(LL a, LL b, LL MOD) {
LL ans = 0;
for(; b > 0; b >>= 1, (a += a)%=MOD)
if(b & 1) (ans += a)%=MOD;
return ans;
}
LL ksm(LL a, LL k, LL MOD) {
LL ans = 1;
for(; k > 0; k >>= 1, a = Mul(a, a, MOD))
if(k&1) ans = Mul(ans, a, MOD);
return ans;
}
void exgcd(LL a, LL b, LL&d, LL &x, LL&y) {
if(!b) { d = a; x = 1; y = 0; }
else {
exgcd(b, a%b, d, y, x);
y = (y - Mul(x, (a/b), r) + r) % r;
}
}
int sd = 2332332;
inline int Ran() { return (sd = (sd*sd+(sd^17737)) & 0x7fffffff); }
const int S = 10;
bool MillerRabin(LL n) {
if(n == 2) return 1;
if(n < 2 || !(n&1)) return 0;
LL t = 0, u = n-1, a, x, y;
while(!(u&1)) ++ t, u>>=1;
for(int i = 0; i < S; ++ i) {
a = Ran() % (n-1) + 1;
x = ksm(a, u, n);
for(int j = 0; j < t; ++ j) {
y = Mul(x, x, n);
if(1 == y && 1 != x && x != n-1) return 0;
x = y;
}
if(x != 1) return 0;
}
return 1;
}
LL fac[100], cnt;
LL abs(LL a) { return a > 0 ? a : -a; }
LL gcd(LL a, LL b) { return b ? gcd(b, a%b) : a; }
LL PollardRho(LL n, LL c) {
LL i = 1, k = 2, x = Ran() % n, y = x, d;
while(1) {
++ i;
x = (Mul(x, x, n) + c) % n;
d = gcd(abs(y-x), n);
if(d != 1 && d != x) return d;
if(x == y) return n;
if(i == k) {
y = x; k <<= 1;
}
}
}
void findfac(LL x) {
if(x == 1) return;
if(MillerRabin(x)) {
fac[++ cnt] = x; return;
}
LL p = x;
while(p >= x) p = PollardRho(p, Ran()%(x-1)+1);
findfac(p); findfac(x/p);
}
LL inv(LL e) {
LL gcd, x, y;
exgcd(e, r, gcd, x, y);
return (((x += r) %= r) += r) %= r;
}
int main() {
scanf("%lld%lld%lld", &e, &N, &c);
findfac(N);
r = (fac[1]-1) * (fac[2]-1);
d = inv(e); LL n = ksm(c, d, N);
printf("%lld %lld\n", d, n);
return 0;
}
Day2T2:额,基本上是裸的Trie,我只想到了在线可持久化的做法,听他们说可以不用可持久化。。。我的做法就是可持久化一下,然后直接在里面查询,如果当前节点之前不能被匹配,就用父亲节点的信息来更新,因为一个节点最多被更新31次,所以可以只开char来保存。我这稍微改一下,用的内存比谁都小,玛德我是智障!
#include <cstdio>
#define MAXN 1000005
struct Node { int l, r; char p; bool stp; } t[MAXN * 30];
int n, rt[MAXN], cnt, sz, len, ans;
unsigned word;
inline int Max(int a, int b) { return a > b ? a : b; }
void Insert(int&r, int p, int d = 31, int f = 0) {
t[r = ++ sz] = t[p];
if(d < len) { if(!t[r].stp) t[r].p = ans + 1; t[r].stp = 1; return; }
if(t[r].stp) ans = t[r].p;
bool b = (1u << d) & word;
if(b) Insert(t[r].r, t[p].r, d-1, r);
else Insert(t[r].l, t[p].l, d-1, r);
}
void Query(int r, int d = 31) {
if(!r) return;
bool b = (1u << d) & word; if(t[r].stp) ans = t[r].p;
if(b) Query(t[r].r, d-1);
else Query(t[r].l, d-1);
}
template<class T>
inline void GET(T&n) {
static char c; n = 0;
do c = getchar(); while('0' > c || c > '9');
do n=n*10+c-'0',c=getchar(); while('0' <= c && c <= '9');
}
int main() {
freopen("route.in", "r", stdin);
freopen("route.out","w",stdout);
GET(n); char op[10]; unsigned a, b, c, d;
int l, r;
for(int i = 1; i <= n; ++ i) {
scanf("%s", op);
if(op[0] == 'A') {
GET(a); GET(b); GET(c); GET(d);
GET(len); word = a << 24 | b << 16 | c << 8 | d;
ans = 0;
++ cnt; len = 32 - len; Insert(rt[cnt], rt[cnt-1]);
}
else {
GET(a); GET(b); GET(c); GET(d); GET(l); GET(r);
word = a << 24 | b << 16 | c << 8 | d;
ans = 0;
Query(rt[r]); int t1 = ans; ans = 0;
Query(rt[l-1]);int t2 = ans;
printf("%d\n", t1 - t2);
}
}
return 0;
}
其他的我也不太会啊,Day1T2 Claris说是要上KD树,找圆内的点数。Day2T3同学说可以直接搜索过,可以证明搜索是O(K)的。。。%%%quack
大家一定记住啊,要算内存的大小!