51Nod 算法马拉松23

A : 打表找规律
B : 数学期望
C : 拓扑排序+DP
D : 状压DP
E : 莫队+树状数组+卡常数
F : Unfinished

听说省选推迟了一个月,整个人都不好了。放假打了一场51Nod,感觉整个人更不好了。


A 1718 “多项式”

能出在第一题的多项式肯定是有奇技淫巧的,打表找了一发规律,发现它的循环节是-1,-2,-1,1,2,1,…

#include<cstdio>
using namespace std;
namespace runzhe2000
{
    long long n; int a[6] = {-1,-2,-1,1,2,1};
    void main()
    {
        scanf("%lld",&n);
        printf("%d",a[(n+4)%6]);
    }
}
int main()
{
    runzhe2000::main();
}
B 1756 谷歌的恐龙

设这一轮 E1 是选完之后就死,这一轮的期望。 E0 是选完之后活的期望。 P=MN

瞎JB推一下,答案 = E1+(1P)E0P

要开long double和输入优化- -

其实好像可以继续推下去的,然而不管啦……反正卡过去了

#include<cstdio>
#define N 10000005
#define getc() ((S == T) && (T = (S = B) + fread(B, 1, MaxBuff, stdin), S == T) ? 0 : *S++)
using namespace std;
namespace runzhe2000
{
    typedef long double ld;
    typedef double db;
    const int MaxBuff = 1 << 20;
    char B[MaxBuff], *S = B, *T = B;
    int read()
    {
        int r = 0; char c = getc();
        for(; c < '0' || c > '9'; c = getc());
        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getc());
        return r;
    }
    int n, m, a[N];
    ld E0,E1, P;
    void main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= m; i++)  a[read()] = 1;
        for(int i = 0; i < n; i++)
        {
            if(a[i]) E1 += (ld) i / m;
            else E0 += (ld) i / (n-m);
        }
        ld P = (ld)m / n;
        printf("%lf\n",(db)(E1+(1-P)*E0/P));
    }
}
int main()
{
    runzhe2000::main();
}
C 1815 调查任务

做得很虚的一道题。不知道我这个做法是不是对的,原来的代码被C题加强数据的第16个hack点卡了好久,多记了一个状态才过的。不知道会不会有别的数据能卡(如果有请务必告诉我^_^),毕竟这种图论题数据类型多种多样…

题意就是最大化路径的次大值。先缩强,记下强联通块内最大值和次大值。

记f[i]表示从S出发到i,能经过的最大值,这个直接DP就行了。

记g[i]表示从S出发到i,路径上经过的最大的次大值,也就是答案所求。考虑转移,大力分类讨论,设j能转移给i。

  1. 如果g[i]选择了点j及j之前的次大值,那直接从g[j]转移过来。
  2. 如果g[i]选择了点j及j之前的最大值,当且仅当i自身是路径最大值,这个用f[i]判一下。且此时那个被转移的最大值一定是f[j],(其实有反例,应该就是那个第16个点,也注意特判此时f[j]=f[i],需要多记h[j]表示除最大值以外的第二大,若f[j]=f[i]则从h[j]转移之类的)。
  3. 如果g[i]选择了i点自身的次大值或最大值,当且仅当f[i]大于i点自身的次大值或最大值,直接转移。

不保证上面口胡的东西是对的,因为调完代码之后我已经忘了我之前在想了什么了……

#include<cstdio>
#include<queue> 
#include<cstring>
#define N 400005
#define M 2000005
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    int n, m, q, s, ecnt, a[N], last[N], bcnt, bel[N], mx[N], mx2[N], deg[N], tdeg[N], f[N], g[N], h[N];
    int timer, dfn[N], low[N], sta[N], insta[N], stacnt;
    struct edge{int next, to;}e[M<<1];
    struct pdge{int a, b;}pe[M];
    void addedge(int a, int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt;
    }
    void tarjan(int x)
    {
        sta[++stacnt] = x; insta[x] = 1; dfn[x] = low[x] = ++timer;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            if(!dfn[y]) {tarjan(y); cmin(low[x], low[y]);}
            else if(insta[y]) cmin(low[x], dfn[y]);
        }
        if(dfn[x] == low[x])
        {
            ++bcnt; int y;
            do
            {
                y = sta[stacnt--];
                bel[y] = bcnt; insta[y] = 0;
                if(a[y] > mx[bcnt]) mx2[bcnt] = mx[bcnt], mx[bcnt] = a[y];
                else if(a[y] > mx2[bcnt] && a[y] != mx[bcnt]) mx2[bcnt] = a[y];
            }
            while(y != x);
        }
    }
    void dp()
    {
        { // dp_mx
            memcpy(tdeg, deg, sizeof(deg)); queue<int> q; q.push(bel[s]);
            for(; !q.empty(); )
            {
                int x = q.front(); q.pop(); cmax(f[x], mx[x]);
                for(int i = last[x]; i; i = e[i].next)
                {
                    int y = e[i].to; 
                    if(f[x] > f[y])
                    {
                        h[y] = f[y];
                        f[y] = f[x];
                    }
                    else if(f[x] != f[y] && f[x] > h[y]) h[y] = f[x];
                    if(!--tdeg[y]) q.push(y);
                }
            }
        }
        { // dp_mx2
            memcpy(tdeg, deg, sizeof(deg)); queue<int> q; q.push(bel[s]);
            for(; !q.empty(); )
            {
                int x = q.front(); q.pop();
                if(mx[x] != f[x]) cmax(g[x], mx[x]);
                else cmax(g[x], mx2[x]);
                for(int i = last[x]; i; i = e[i].next)
                {
                    int y = e[i].to;
                    cmax(g[y], g[x]);
                    if(f[y] == mx[y] && f[x] != f[y]) cmax(g[y], f[x]);
                    if(f[y] == mx[y] && h[x] != f[y]) cmax(g[y], h[x]);
                    if(!--tdeg[y]) q.push(y);
                }
            }
        }
    }
    void main()
    {
        scanf("%d%d%d%d",&n,&m,&q,&s);
        for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
        for(int i = 1, a, b; i <= m; i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a, b);
            pe[i] = (pdge){a, b};
        }
        tarjan(s); ecnt = 0;
        memset(last, 0, sizeof(last));
        for(int i = 1; i <= m; i++)
        {
            if(bel[pe[i].a] != bel[pe[i].b] && bel[pe[i].a])
            {
                addedge(bel[pe[i].a], bel[pe[i].b]);
                deg[bel[pe[i].b]]++;
            }
        }
        deg[bel[s]] = 0;
        dp();
        for(int i = 1; i <= q; i++)
        {
            int x; scanf("%d",&x);
            printf("%d ",dfn[x]?g[bel[x]]:-1);
        }
    }
}
int main()
{
    runzhe2000::main();
}
D 1683 最短路

写了比较长的一段题解,然后消失了,好气啊。那就再口胡一遍吧。n很小,对列状压,我们记 f[i][j1][j2]...[jn] 表示到第i列的第1,2,…n行的最短路分别是j1,j2,…jn时的方案数,枚举下一列的状态转移。j1,j2…jn的状态数并不多,因为显然相邻两格最短路不超过1。j1,j2,…jn可以哈希起来。转移要先预处理,要不然可能会T。

#include<cstdio>
#include<algorithm>
#define N 7
#define M 105
#define H 250
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<29;
    int n, m, mod, sta, tot, a[N], b[N], d[N], f[M][M+N][H], ans[M+N];
    struct trans{int to, up;}tr[H][1<<N];
    int packin(int *a)
    {
        int ret = 0;
        for(int i = 2; i <= n; i++) ret = ret * 3 + (a[i] + 1);
        return ret;
    }
    int packout(int *a, int r)
    {
        for(int i = n; i >= 2; i--) a[i] = r % 3 - 1, r /= 3;
        return r;
    }
    void main()
    {
        scanf("%d%d%d",&n,&m,&mod); sta = 1<<n;
        for(int i = 2; i <= n; i++) a[i] = 1; tot = packin(a);
        for(int i = 0; i <= tot; i++)
        {
            packout(a, i);
            for(int k = 2; k <= n; k++) a[k] += a[k-1];
            for(int j = 0; j < sta; j++)
            {
                for(int k = 1; k <= n; k++) b[k] = (j>>(k-1))&1;
                for(int k = 1; k <= n; k++) d[k] = a[k] + b[k];
                for(int k = 1; k <= n; k++)
                {
                    for(int h = k-1; h >= 1; h--) d[h] = min(d[h], d[h+1] + b[h]);
                    for(int h = k+1; h <= n; h++) d[h] = min(d[h], d[h-1] + b[h]);
                }
                for(int k = n; k > 1; k--) d[k] -= d[k-1];
                tr[i][j] = (trans){packin(d), d[1]};
            }
        }
        for(int i = 0; i < sta; i++)
        {
            for(int k = 1; k <= n; k++) b[k] = (i>>(k-1))&1;
            int s = packin(b);
            f[1][b[1]][s]++;
        }
        for(int i = 1; i < m; i++)
            for(int j = 0, jj = n+m; j < jj; j++)
                for(int k = 0; k <= tot; k++) if(f[i][j][k])
                    for(int s = 0; s < sta; s++)
                        (f[i+1][j+tr[k][s].up][tr[k][s].to] += f[i][j][k]) %= mod;


        for(int i = 0; i <= m; i++)
            for(int j = 0; j <= tot; j++)
            {
                packout(a, j);
                for(int k = 2; k <= n; k++) a[k] += a[k-1];
                (ans[i+a[n]] += f[m][i][j]) %= mod;
            }
        for(int i = 0, ii = n+m; i < ii; i++) printf("%d\n",ans[i]);
    }
}
int main()
{
    runzhe2000::main();
}
E 1592 数列积

这是一个不知道正解的人的卡常数(log)的心酸历程

绝对值很麻烦,肯定要拆,那么就分开考虑 ai<aj ai>aj 的。考虑到这个信息很难合并,想想莫队。

考虑加入一个元素之后发生了什么变化。设在区间 [l,r] 的尾部加了一个 a[j] 进来,其它位置的删/加类似。

我们先假设只有 ai<aj 的情况,那答案就会增加 ri=l(ajai)(ji) ,那我们可以维护四个信息()详见代码)来快速算出这个增量。对于 ai>aj ,维护另一套信息即可。

然后我写了一个主席树,这样是 O(nn log n) 。卡了一个下午的常数,愣是没卡过去,当时整个人都不好了。

后来考虑 O(nn) 的做法,感觉可以上分块数据结构,也不知道对不对。

之后得知把主席树改成树状数组就能快很多。好吧,我naive了,改改改,拼命加优化。

再后来,我就也卡过去了…..

#include<cmath> 
#include<ctime>
#include<cstdio>
#include<algorithm>
#define N 50005
#define lowbit(_i)  (_i&-_i)
#define R register
using namespace std;
namespace runzhe2000
{
    typedef unsigned long long ull;
    int read()
    {
        int r = 0; char c = getchar();
        for(; c < '0' || c > '9'; c = getchar());
        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
        return r;
    }
    int n, S, qcnt, mx, a[N], block[N], ccnt;
    ull out[N];
    struct que
    {
        int l, r, id;
        bool operator < (const que &that) const 
        {
            return block[l] != block[that.l] 
                ? block[l] < block[that.l] 
                : (block[l] & 1) ? r < that.r : that.r < r;
        }
    }q[N];
    struct BIT
    {
        ull t[N<<1];
        ull ask(R int x){R ull r = 0; for(; x; x -= lowbit(x)) r += t[x]; return r;}
        void mod(R int x, R ull v){for(; x <= mx; x += lowbit(x)) t[x] += v;}
    } tsum, tsum_i, tsum_a, tsum_1;
    ull add(int j)
    {
        int pos = a[j];
        ull ret = 0, sum = tsum.ask(pos), sum_i = tsum_i.ask(pos), sum_a = tsum_a.ask(pos), sum_1 = tsum_1.ask(pos);
        ret += -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
        sum = tsum.ask(mx)-sum; sum_i = tsum_i.ask(mx)-sum_i; sum_a = tsum_a.ask(mx)-sum_a; sum_1 = tsum_1.ask(mx)-sum_1;
        ret -= -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
        tsum.mod(pos, (ull)(n-j)*pos);
        tsum_i.mod(pos, n-j);
        tsum_a.mod(pos, pos);
        tsum_1.mod(pos, 1);
        return ret;
    }
    ull del(int j)
    {
        int pos = a[j];
        tsum.mod(pos, (ull)(j-n)*pos);
        tsum_i.mod(pos, -n+j);
        tsum_a.mod(pos, -pos);
        tsum_1.mod(pos, -1);
        ull ret = 0, sum = tsum.ask(pos), sum_i = tsum_i.ask(pos), sum_a = tsum_a.ask(pos), sum_1 = tsum_1.ask(pos);
        ret -= -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
        sum = tsum.ask(mx)-sum; sum_i = tsum_i.ask(mx)-sum_i; sum_a = tsum_a.ask(mx)-sum_a; sum_1 = tsum_1.ask(mx)-sum_1;
        ret += -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
        return ret;
    }
    void main()
    {
        n = read(); S = sqrt((double)n);
        for(int i = 1; i <= n; i++) a[i] = read(), block[i] = i % S == 1 ? block[i-1]+1 : block[i-1], mx < a[i] ? mx = a[i] : 0; mx++;
        qcnt = read();  for(int i = 1; i <= qcnt; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
        sort(q+1, q+1+qcnt); R int lef = 1, rig = 0, l, r; ull ans = 0; 
        for(int i = 1; i <= qcnt; i++)
        {
            l = q[i].l, r = q[i].r;
            for(; l < lef; lef--) ans -= add(lef-1);
            for(; rig < r; rig++) ans += add(rig+1);
            for(; lef < l; lef++) ans -= del(lef);
            for(; r < rig; rig--) ans += del(rig);
            out[q[i].id] = ans;
        }
        for(int i = 1; i <= qcnt; i++) printf("%llu\n",out[i]);
    }
}
int main()
{
    runzhe2000::main();
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值