CQOI2016爆炸记

要试题的话,我现在只有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,第 i1 位是 k ,是否有4,是否有8,是否已近合法,这样的转移状态数是 O(111010222) 乘上一个枚举的次数,
所以最坏时间复杂度大约是 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
大家一定记住啊,要算内存的大小!

你可能感兴趣的:(CQOI2016爆炸记)