BZOJ 2017省队十连测推广赛1

来源:互联网 发布:数据分析论坛推荐 编辑:程序博客网 时间:2024/05/29 07:09

A 4765 : 分块
B 4766 : 矩阵树定理或找规律
C 4767 : 容斥+DP

感觉难度应该是A>B>C?
可惜周二晚上在训练,不然也许就能全A了……


A 4765 普通计算姬

分块分块再分块……
想了一想感觉没什么树上的算法能用,考虑分块乱搞。把操作分根号块,每次进入新块时重建一整棵树。那么一次询问至多只会对应根号个修改。对于每一个点x,记录从x到根的路径上经过了1~n的哪些点,这同样也可以分块,对于根号个修改点直接查贡献即可。因为分块了,查完之后还会有一些块外小点没统计到。因小点不超过根号个,对于这些小点就暴力查子树和,这需要一种修改O(n) 询问O(1)的数据结构,同样还是可以分块……总时间复杂度O(nn)

#include<cstdio>#include<cstring>#define S 900#define N 200005#define ll unsigned long long#define R registerusing namespace std;namespace runzhe2000{    inline int read()    {        R int r = 0; R char c = getchar();        for(; c < '0' || c > '9'; c = getchar());        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());        return r;    }    ll sum[N], big[N], small[N];    int n, m, ecnt, d[N], last[N], root, beg[N], end[N], timer, fa[N], cnt[N][N/S+10], q[N], qv[S+10], qtop, bel[N], rebeg[N];    struct edge{int next, to;}e[N<<1];    void addedge(int a, int b)    {        e[++ecnt] = (edge){last[a], b};        last[a] = ecnt;    }    struct block    {        ll big[N/S+10],small[N];        block(){memset(big,0,sizeof big); memset(small,0,sizeof small);}        void init()        {            memset(big,0,N<<3);            memset(small,0,N<<3);        }        void add(int x, int v)        {            for(int i = bel[x], ii = N/S+10; i < ii; i++)                big[i] += v;            for(int i = x, ii = S*bel[x]; i <= ii; i++)                small[i] += v;         }        ll query(int x)        {            if(!x) return 0;            return big[bel[x]-1] + small[x];        }    }blo;    void init_sum(int x)    {        sum[x] = d[x];        for(int i = last[x]; i; i = e[i].next)        {            int y = e[i].to;            if(y == fa[x]) continue;            init_sum(y);            sum[x] += sum[y];        }    }    void dfs(int x)    {        rebeg[beg[x] = ++timer] = x;        memcpy(cnt[x], cnt[fa[x]], (N/S+10)<<2);        for(int i = bel[x], ii = N/S+10; i < ii; i++) cnt[x][i]++;        for(int i = last[x]; i; i = e[i].next)        {            int y = e[i].to;            if(y == fa[x]) continue;            fa[y] = x; dfs(y);        }        end[x] = timer;    }    ll calc(int lim)    {        if(!lim) return 0;        R ll ret = sum[lim];        for(R int i = 1; i <= qtop; i++)            ret += (ll)cnt[q[i]][bel[lim]-1] * qv[i];        for(int i = S*(bel[lim]-1)+1; i <= lim; i++)            ret += blo.query(end[i]) - blo.query(beg[i]-1);        return ret;    }    void main()    {        n = read(); m = read();        for(int i = 1; i <= n; i++)        {            d[i] = read();            bel[i] = bel[i-1] + (i%S==1?1:0);        }        for(int i = 1, a, b; i <= n; i++)        {            a = read(); b = read();            if(!a) {root = b; continue;}            if(!b) {root = a; continue;}            addedge(a, b);            addedge(b, a);        }        dfs(root);        blo.init();        for(R int i = 1, op, u, v; i <= m; i++)        {            op = read(), u = read(), v = read();            if(i % S == 1)            {                blo.init();                 memset(sum,0,N<<3);                init_sum(root);                for(R int i = 1; i <= n; i++) sum[i] += sum[i-1];                qtop = 0;            }            if(op == 1)            {                 q[++qtop] = u; qv[qtop] = v - d[u];                blo.add(beg[u], v - d[u]);                d[u] = v;            }            else                printf("%llu\n",calc(v) - calc(u-1));        }    }}int main(){    runzhe2000::main();}

B 4766 文艺计算姬

考场上打表找出规律,答案为 nm1mn1。然后CJK学长教我用矩阵树定理直接推出来,orz orz orz。这里画矩阵不方便,就不写过程了。

#include<cstdio>#include<cstring>#define ll long longusing namespace std;namespace runzhe2000{    ll n, m, p;    ll mul(ll a, ll b)    {        ll r = 0;        for(; a; a >>= 1)        {            if(a & 1) r = (r + b) % p;            b = (b + b) % p;        }        return r;    }    ll fpow(ll a, ll b)    {        ll r = 1;        for(; b; b >>= 1)        {            if(b&1) r = mul(r, a);            a = mul(a, a);        }        return r;    }    void main()    {        scanf("%lld%lld%lld",&n,&m,&p);        printf("%lld\n",mul(fpow(n,m-1),fpow(m,n-1)));    }}int main(){    runzhe2000::main();}

C 4767 两双手

把基底变成(0,1)(1,0),点坐标就好多了。显然容斥,记f[i][0/1]表示此时在i,已经至少经过的点数为偶数/奇数个时的方案数。由于图是拓扑图,可以直接DP出来。

#include<map>#include<cstdio>#include<algorithm>#define mkp(u,v) make_pair(u,v) #define N 505#define M 666666#define MOD 1000000007using namespace std;namespace runzhe2000{    typedef long long ll;    const double eps = 1e-3;    struct point{int x, y;}p[N];    int Ex, Ey, Ax, Ay, Bx, By, n, ecnt, pcnt, f[N][2], fac[M], inv[M], inc[M], deg[N], q[N], last[N];    struct edge{int next, to;}e[N*N];    map<pair<int,int>,bool> vis;    void addedge(int a, int b)    {        e[++ecnt] = (edge){last[a], b};        last[a] = ecnt;    }    bool addpoint(int x, int y)    {        int nx = By ?             ((double)y * Bx / By - x) / ((double)Ay * Bx / By - Ax) + eps:            ((double)x * By / Bx - y) / ((double)Ax * By / Bx - Ay) + eps;        int ny = Ax ?             ((double)x * Ay / Ax - y) / ((double)Bx * Ay / Ax - By) + eps:            ((double)y * Ax / Ay - x) / ((double)By * Ax / Ay - Bx) + eps;        if(x != nx * Ax + ny * Bx || y != nx * Ay + ny * By || nx < 0 || ny < 0 || vis[mkp(nx,ny)]) return false;        vis[mkp(nx,ny)] = 1;        p[++pcnt] = (point){nx, ny}; return true;    }    int C(int a, int b){return (ll)fac[a] * inc[b] % MOD * inc[a-b] % MOD;}    void main()    {        fac[0] = fac[1] = inv[1] = inc[1] = inc[0] = 1;        for(int i = 2; i < 666666; i++)        {               fac[i] = (ll)fac[i-1] * i % MOD;            inv[i] = (ll)(MOD - MOD/i) * inv[MOD % i] % MOD;            inc[i] = (ll)inc[i-1] * inv[i] % MOD;        }        scanf("%d%d%d%d%d%d%d",&Ex,&Ey,&n,&Ax,&Ay,&Bx,&By);        addpoint(0,0);        for(int i = 1, x, y; i <= n; i++)        {            scanf("%d%d",&x,&y);            if(x == 0 && y == 0){puts("0"); return;}            if(x == Ex && y == Ey){puts("0"); return;}            addpoint(x,y);        }        if(Ex == 0 && Ey == 0) {puts("1"); return;}        if(!addpoint(Ex,Ey)) {puts("0"); return;}        for(int i = 1; i <= pcnt; i++)   for(int j = 1; j <= pcnt; j++) if(i != j && p[i].x <= p[j].x && p[i].y <= p[j].y) addedge(i, j), deg[j]++;        q[0] = 1; f[1][1] = 1;        for(int head=0, tail=1; head<tail; head++)        {            int x = q[head];            for(int i = last[x]; i; i = e[i].next)            {                int y = e[i].to, P = C(p[y].y - p[x].y + p[y].x - p[x].x, p[y].x - p[x].x);                (f[y][0] += (ll)f[x][1] * P % MOD) %= MOD;                 (f[y][1] += (ll)f[x][0] * P % MOD) %= MOD;                 if(!(--deg[y])) q[tail++] = y;            }        }        printf("%d\n",((f[pcnt][0] - f[pcnt][1]) % MOD + MOD) % MOD);    }}int main(){    runzhe2000::main();}
0 0