2018.1.14 省选模拟赛

     挺好的一场比赛,结果被我考得像*一样.

     第一次打这种5个小时的比赛,想着时间还比较充足,就花了比较长的时间读题,预计自己应该能得40+80+50,如果中途想到了正解分数可能会更高.初步感觉T1和T3的暴力挺好打的,只是这个T2......预感到自己将花一半的时间在这道题上.

       先打T1的暴力,不到10min就打完了,测了测样例一遍就AC了,检查了一下没有发现什么问题.挺开心的,但是还是不放行,于是输出了几个中间值,发现和预期的完全不同.接着测了自己造几个小数据,都碰巧过了,但仅仅只是碰巧,中途计算的结果还是错的,如果直接这样交上去可能就没分了.于是去上了个厕所,突然想起来ST表求区间异或是错的!会把重叠部分给多统计!一开始还一直以为是ST表写错了.那么也没办法了,就打了一个线段树上去,常数大,又带一个log,当时还担心1000的数据会不会T,事实证明担心是多余的.

       T2想着暴力有80分,都没去想正解(其实是不敢想,毕竟下意识里认为我做不出省选难度的第二题嘛),结果没想到正解比暴力还简单.慢慢地1h过去了,写了300+行线段树,写到身心俱疲,心力憔悴.前60%的点我造了几组小数据都过了,结果后面20%的一直写不对.改了好长时间,大概有2h吧,到最后整个思路都混乱了,但我觉得我的做法明明就是正确的,一定不会错,再给我一点时间肯定能找到错误.果然,原来是一种情况b值的变化没有考虑到,下意识地以为和前面的相同.改过来就对了,测了好几组小数据都过了.终于放心了.然而最后的结果令人大吃一惊.我认为不可能错的恰好错了,可能错的却没错.原因是:我测试的数据全部都是小数据!当我把用大数据测时,我发现我的程序输出了好多负数,绝对是爆int了.可是我的程序里基本上全都是long long,就连读入的变量都是long long,怎么会爆int呢?于是我ctrl + F查找int,发现结构体里面有几个变量定义的是int......MDZZ.可是后20%的点还是不对啊......对于答案输出和我的输出,非常奇怪,前面一些点的输出都是一样的,到后面就完全不一样了.查错多时,无果.向老师求助,老师一眼就看出了我的错误:除法取模.MDZZ.我竟然会犯这种低级错误......当时直接写的表达式,表达式一长,取模操作就容易错,真应该听老师的话:表达式拆开来写.

       改完T2就没时间写T3了,其实T3的暴力分特别好拿,白丢了50分.匆忙写的dfs还WA掉了.发现我在写用dfs求最长路的时候忘了清零vis数组了......

题解:

 

1. 简易比特币

 

题目描述

 

    相信大家都听说过比特币。它是一种虚拟货币,但与普通虚拟货币不同的是,它不由某个机构统一发行,而需要利用计算机找出具有特定性质的数据来“发现”货币,俗称“挖矿”。然而,由于具有这种特定性质的数据分布稀疏而无规律,因此挖矿的过程需要投入大量的计算资源来搜寻这些数据。

 

    仿照比特币是设计思想,我们可以设计一种简易的比特币:给定一个由n个非负整数构成的序列{ai},和一个阈值K,如果某个非空子序列(一个连续的区间)中的所有数的异或和小于K,则这个子序列就对应了一个比特币,否则它毫无价值。

 

       现在,给出这个序列和阈值,请你计算从中能获得多少个比特币。

 

 

 

    严谨起见,简要解释一下什么是异或:

 

异或是一种位运算,Pascal中写作xor,C/C++中写作^。将两个数写成二进制形式,然后对每位作“相同得0、不同得1”的运算。例如,12 xor 6 = 10的运算方法如下:

 

12 = (1100)2

 

6   = (0110)2

 

ans= (1010)2 = 10

 

 

 

输入格式

 

       第一行包含两个整数n和K,意义如题所述;

 

       第二行包含n个非负整数ai,表示序列中的每一个数。

 

 

 

输出格式

 

       一行包含一个整数,表示能从序列中获得的比特币数。

 

 

 

样例输入

 

3 2

 

1 3 2

 

 

 

样例输出

 

3

 

 

 

样例解释

 

1 = 1

 

1 xor 3 = 2

 

1 xor 3 xor 2 = 0

 

3 = 3

 

3 xor 2 = 1

 

2 = 2

 

一共3个区间的异或和小于2。

 

 

 

数据范围

 

对于20%的数据,n≤100;

 

对于40%的数据,n≤1000;

 

另有20%的数据,ai≤50;

 

对于100%的数据,1≤n≤105,0≤K≤109,0≤ai≤109

分析:Trie树上一个比较神的应用.

          做这道题之前需要思考另外一道题:给定n个数,再给一个数c,问有多少数小于c.这道题可以用很多方法水过,不过一种神奇的方法是利用trie+二进制.二进制数比较大小是从高位到低位比较的.将n个数都转化为二进制,插入到trie中.然后将c在trie上跑.如果c的第i位为1,则ans += 下一步走"0"边到达的点的点权(也就是这一位是0,与c不匹配了,那么通过这一位衍生出的数都小于c),c往边"1"上走,否则c直接往边"0"上走.

          这道题也是一样的原理.只不过需要对问题转化一下.对于每个点,求出它的前缀异或值sum[i],那么对于一个区间[l,r]的异或值就是sum[r] ^ sum[l - 1],因为[1,l-1]这段区间被异或了2次,值为0,这也是一个非常重要的思想:区间上的值转化为两个端点的值.接着枚举每一个数,先统计答案再插入.假设当前插入的数的第i位为x,那么如果接下来走"x"边,那么下一位就为0,如果走"x^1"边,下一位就为1,依据上一题的原理,就可以统计了.

          部分分解法:40%,线段树预处理出区间异或值,枚举区间,区间查询. 60%,数字很小,前缀异或值的范围是0~63,那么预处理出有多少对数的异或值小于k,然后在序列中扫,统计个数(具体看代码).

完整代码:(包含了部分分+AC代码)

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 100010;

int n,k,a[maxn],tot = 1;
int map[110][110],num[110];
long long sum[maxn << 2],ans,jie[40];
bool flag = true;

struct node
{
    int tr[3],sum;
}e[maxn * 30];

void pushup(int o)
{
    sum[o] = sum[o * 2] ^ sum[o * 2 + 1];
}

void build(int o,int l,int r)
{
    if (l == r)
    {
        sum[o] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(o * 2,l,mid);
    build(o * 2 + 1,mid + 1,r);
    pushup(o);
}

long long query(int o,int l,int r,int x,int y)
{
    if (x <= l && r <= y)
        return sum[o];
    int mid = (l + r) >> 1;
    long long res = 0;
    if (x <= mid)
        res = query(o * 2,l,mid,x,y);
    if (y > mid)
    {
        long long temp = query(o * 2 + 1,mid + 1,r,x,y);
        if (res == 0)
            res = temp;
        else
            res ^= temp;
    }
    return res;
}

void solve()
{
    sum[1] = a[1];
    for (int i = 2; i <= n; i++)
    sum[i] = sum[i - 1] ^ a[i];
    for (int i = 0; i < 64; i++)
        for (int j = 0; j < 64; j++)
            if ((i ^ j) < k)
                map[i][j] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < 64; j++)
        {
            if (map[sum[i]][j])
                ans += num[j];
        }
        if (sum[i] < k)
            ans++;
        num[sum[i]]++;
    }
    cout << ans << endl;
}

void solve2()
{
    build(1,1,n);
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            if (query(1,1,n,i,j) < k)
                ans++;
    for (int i = 1; i <= n; i++)
        if (a[i] < k)
            ans++;
    cout << ans << endl;
}

void insert(long long x)
{
    int now = 1;
    for (int i = 30; i >= 0; i--)
    {
        bool temp = x & jie[i];
        if (!e[now].tr[temp])
            e[now].tr[temp] = ++tot;
        now = e[now].tr[temp];
        e[now].sum++;
    }
}

long long query(long long x)
{
    long long res = 0;
    int now = 1;
    for (int i = 30; i >= 0; i--)
    {
        if (now == 0)
            return res;
        bool temp = x & jie[i];
        bool temp2 = temp ^ 1;
        if (k & jie[i])
        {
            res += e[e[now].tr[temp]].sum;
            now = e[now].tr[temp2];
        }
        else
            now = e[now].tr[temp];
    }
    return res;
}

void solve3()
{
    jie[0] = 1;
    for (int i = 1; i <= 30; i++)
        jie[i] = jie[i - 1] * 2;
    long long res = 0;
    insert(0);
    for (int i = 1; i <= n; i++)
        {
            res ^= a[i];
            ans += query(res);
            insert(res);
        }
    cout << ans << endl;
}

int main()
{
    scanf("%d%d",&n,&k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);
        if (a[i] > 50)
        flag = false;
    }
    if (flag)
    solve(); //20%
    else
        if (n <= 1000)
            solve2(); //40%
    else
        solve3();
    
    return 0;
}

 

2.计算

问题描述

       我曾经的竞赛教练有一句名言:“人紧张起来的时候会变得和白痴一样的。”他总爱在比赛前重复这句话。其实论算法,他并没有教给我们多少,但是回想起以前的经历发现,至少这句话他说的真是太tm对了。用现在的话讲就是:不要怂,就是干。

oi题很多时候都是这样,乍一看很难,越看越觉得不可做,于是安慰自己说,肯定又是我没学过的某算法,做不出很正常。但抱有这种心理的,出了考场往往会被身边的神犇打脸:“这题其实先oo一下再xx一下就好了,我太弱了搞了一小时才搞出来……”

现在就有一道看上去似乎很不好搞的计算题,请你不怂地算一下怎么搞。

给出一个长为N的正整数序列,有三种操作:

A l r k b:在区间[l,r]上加上一个首项为b、公差为k的等差数列。即,序列al, al+1, al+2, al+3……变成al+b, al+1+b+k, al+2+b+2k, al+3+b+3k……

B l r:求区间[l,r]内所有数的和mod 1000000007的值

C l r:求区间[l,r]内所有数的平方的和mod 1000000007的值

 

输入格式

         第一行包含两个数n、q,表示序列长度和操作的数量;

         第二行包含n个数{ai},表示原序列;

         接下来q行,每行包含一个操作,格式和意义如题面所述。

 

输出格式

         输出若干行,每个B操作和C操作输出一行,表示询问的答案。

 

样例输入

3 3

1 1 1

A 1 3 2 2

B 1 2

C 2 3

 

样例输出

8

74

 

数据规模

测试点1~2:n, q ≤ 1000;

测试点3~4:k=0,没有C操作;

测试点5~6:k=0;

测试点7~8:没有C操作;

对于100%的数据,n, q ≤ 100000,0 ≤ ai, k, b ≤ 109,1 ≤ l ≤ r ≤ n

分析:部分分解法:

  20%:直接暴力.

  40%,区间加,区间求和.

  60%:区间加,区间求和,区间求平方和,把式子展开一下就好了,坑点:要先维护平方和,再维护和.

  80%:等差数列区间加,区间求和. 一个标记维护两个内容:在当前区间生效的K值和B值.标记的下传:子区间的k,b直接加上父节点的k,b,不过对于右子区间的b有变化!这是需要特别注意的一点:增加b值会有偏移!

  100%:通用方法:式子展开.看哪些信息已经维护了,哪些信息还需要维护.维护一下就好了.这里给出最终需要维护的标记:

 

 

完整代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;
const ll maxn = 100010,mod = 1000000007;

ll n,q;
bool flag1 = true,flag2 = true;
ll a[maxn],sum[maxn << 2],tag[maxn << 2],L[maxn << 2],R[maxn << 2],sum2[maxn << 2],cnt[maxn << 2],K[maxn << 2],B[maxn << 2],inc[maxn << 2];

struct node
{
        ll id,k,b,l,r; 
}e[maxn];

ll qumo(ll a,ll b)
{
    if (a % 2 == 0)
    a /= 2;
    else
    b /= 2;
    a %= mod;
    b %= mod;
    return a * b % mod;
}

void solve1()
{
    for (ll i = 1; i <= q; i++)
    {
        if (e[i].id == 1)
        {
            ll temp = e[i].b;
            for (ll j = e[i].l; j <= e[i].r; j++)
            {
                a[j] += temp;
                a[j] %= mod;
                temp += e[i].k;
                temp %= mod;
            }
        }
        if (e[i].id == 2)
        {
            ll res = 0;
            for (ll j = e[i].l; j <= e[i].r; j++)
            {
                res += a[j];
                res %= mod;
            }
            printf("%lld\n",res);
        }
        if (e[i].id == 3)
        {
            ll res = 0;
            for (ll j = e[i].l; j <= e[i].r; j++)
            {
                res += a[j] * a[j] % mod;
                res %= mod;
            }
            printf("%lld\n",res);
        }
    }
}

void pushup(ll o)
{
    sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % mod;
    sum2[o] = (sum2[o * 2] + sum2[o * 2 + 1]) % mod;
    inc[o] = inc[o * 2] + inc[o * 2 + 1] + (R[o * 2] - L[o * 2] + 1) * sum[o * 2 + 1] % mod;
    inc[o] %= mod;
}

void build(ll o,ll l,ll r)
{
    L[o] = l;
    R[o] = r;
    if (l == r)
    {
        sum[o] = a[l];              
        sum[o] %= mod;
        sum2[o] = a[l] * a[l] % mod;
        return;
    }
    ll mid = (l + r) >> 1;
    build(o * 2,l,mid);
    build(o * 2 + 1,mid + 1,r);
    pushup(o);
}

void pushdown(ll o)
{
    if (tag[o])
    {
        tag[o * 2] += tag[o];
        tag[o * 2] %= mod;
        tag[o * 2 + 1] += tag[o];
        tag[o * 2 + 1] %= mod;
        ll temp1 = ((tag[o] * tag[o]) % mod) * (R[o * 2] - L[o * 2] + 1) % mod;
        sum2[o * 2] += temp1 + (2 * tag[o] * sum[o * 2])% mod;
        sum2[o * 2] %= mod;
        ll temp2 = ((tag[o] * tag[o]) % mod) * (R[o * 2 + 1] - L[o * 2 + 1] + 1) % mod;
        sum2[o * 2 + 1] += temp2 + 2 * tag[o] * sum[o * 2 + 1] % mod;
        sum2[o * 2 + 1] %= mod;
        sum[o * 2] += tag[o] * (R[o * 2] - L[o * 2] + 1) % mod;
        sum[o * 2] %= mod;
        sum[o * 2 + 1] += tag[o] * (R[o * 2 + 1] - L[o * 2 + 1] + 1) % mod;
        sum[o * 2 + 1] %= mod;
        tag[o] = 0;
    }
}

void update(ll o,ll l,ll r,ll x,ll y,ll v)
{
    if (x <= l && r <= y)
    {
        tag[o] += v;
        tag[o] %= mod;
        ll temp = ((v * v) % mod) * (r - l + 1) % mod;
        sum2[o] += temp + 2 * v * sum[o] % mod;
        sum2[o] %= mod;
        sum[o] += v * (r - l + 1) % mod;
        sum[o] %= mod;
        return;
    }
    pushdown(o);
    ll mid = (l + r) >> 1;
    if (x <= mid)
        update(o * 2,l,mid,x,y,v);
    if (y > mid)
        update(o * 2 + 1,mid + 1,r,x,y,v);
    pushup(o);
}

ll query(ll o,ll l,ll r,ll x,ll y)
{
    if (x <= l && r <= y)
        return sum[o];
    pushdown(o);
    ll mid = (l + r) >> 1;
    ll res = 0;
    if (x <= mid)
        res += query(o * 2,l,mid,x,y);
    res %= mod;
    if (y > mid)
        res += query(o * 2 + 1,mid + 1,r,x,y);
    res %= mod;
    return res;
}

ll query2(ll o,ll l,ll r,ll x,ll y)
{
    if (x <= l && r <= y)
        return sum2[o];
    pushdown(o);
    ll mid = (l + r) >> 1;
    ll res = 0;
    if (x <= mid)
        res += query2(o * 2,l,mid,x,y);
    res %= mod;
    if (y > mid)
        res += query2(o * 2 + 1,mid + 1,r,x,y);
    res %= mod;
    return res;
}

void solve2()
{
    build(1,1,n);
    for (ll i = 1; i <= q; i++)
    {
        if (e[i].id == 2)
            printf("%lld\n",query(1,1,n,e[i].l,e[i].r));
        else
            if (e[i].id == 1)
                update(1,1,n,e[i].l,e[i].r,e[i].b);
            else
                if (e[i].id == 3)
                    printf("%lld\n",query2(1,1,n,e[i].l,e[i].r));
    }
}

void pushdown2(ll o)
{
    if (cnt[o] != 0)
    {
        cnt[o * 2] = cnt[o * 2 + 1] = 1;
        cnt[o] = 0;
        K[o * 2] += K[o];
        K[o * 2] %= mod;
        K[o * 2 + 1] += K[o];
        K[o * 2 + 1] %= mod;
        B[o * 2] += B[o];
        B[o * 2] %= mod;
        B[o * 2 + 1] += (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod;
        B[o * 2 + 1] %= mod;
        ll tempp = qumo(B[o] + B[o] + (R[o * 2] - L[o * 2]) * K[o],R[o * 2] - L[o * 2] + 1);
        sum[o * 2] += tempp;
        sum[o * 2] %= mod;
        ll temp = (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; //右半区间的b 
        tempp = qumo(temp + temp + (R[o * 2 + 1] - L[o * 2 + 1]) * K[o],R[o * 2 + 1] - L[o * 2 + 1] + 1);
        sum[o * 2 + 1] += tempp;
        sum[o * 2 + 1] %= mod;
        K[o] = 0;
        B[o] = 0;
    }
}

void update2(ll cur,ll o,ll l,ll r,ll x,ll y,ll k,ll b)
{
    if (x <= l && r <= y)
    {
        cnt[o] = 1;
        K[o] += k;
        K[o] %= mod;
        b = b + (l - e[cur].l) * k % mod;
        b %= mod;
        B[o] += b;
        B[o] %= mod;
        ll res1 = (r - l) * k + b + b;
        ll res2 = r - l + 1;
        if (res1 % 2 == 0)
        res1 /= 2;
        else
        res2 /= 2;
        res1 %= mod;
        res2 %= mod;
        ll temp = qumo(b + b + ((r - l) * k),(r - l + 1));
        temp %= mod;
        sum[o] += temp;
        sum[o] %= mod;
        return;
    }
    pushdown2(o);
    ll mid = (l + r) >> 1;
    if (x <= mid)
        update2(cur,o * 2,l,mid,x,y,k,b);
    if (y > mid)
        update2(cur,o * 2 + 1,mid + 1,r,x,y,k,b);
    pushup(o);
}

ll query3(ll o,ll l,ll r,ll x,ll y)
{
    if (x <= l && r <= y)
        return sum[o];
    pushdown2(o);
    ll res = 0;
    ll mid = (l + r) >> 1;
    if (x <= mid)
        res += query3(o * 2,l,mid,x,y);
    res %= mod;
    if (y > mid)
        res += query3(o * 2 + 1,mid + 1,r,x,y);
    res %= mod;
    return res;
}

void solve3()
{
    build(1,1,n);
    for (ll i = 1; i <= q; i++)
    {
        if (e[i].id == 2)
            printf("%lld\n",query3(1,1,n,e[i].l,e[i].r));
        else
            update2(i,1,1,n,e[i].l,e[i].r,e[i].k,e[i].b);
    }
}

void pushdown3(ll o)
{
    if (cnt[o] != 0)
    {
        cnt[o * 2] = cnt[o * 2 + 1] = 1;
        cnt[o] = 0;
        K[o * 2] += K[o];
        K[o * 2] %= mod;
        K[o * 2 + 1] += K[o];
        K[o * 2 + 1] %= mod;
        B[o * 2] += B[o];
        B[o * 2] %= mod;
        B[o * 2 + 1] += (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod;
        B[o * 2 + 1] %= mod;
        //史上最痛苦的时刻:
        ll len1 = (R[o * 2] - L[o * 2] + 1),len2 = (R[o * 2 + 1] - L[o * 2 + 1] + 1);
        ll tempp =  (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; //右半部分的b!
        ll temp = 2 * B[o] * sum[o * 2] % mod;
        temp = (temp + 2 * K[o] * inc[o * 2] % mod) % mod;
        temp = (temp + B[o] * B[o] % mod * len1 % mod) % mod;
        temp = (temp + K[o] * B[o] % mod * (len1 - 1) % mod * len1 % mod) % mod;
        ll temp2 = (len1 - 1) * len1 * (2 * len1 - 1) / 6 % mod;
        temp2 = temp2 * K[o] % mod * K[o] % mod;
        temp = (temp + temp2) % mod;
        sum2[o * 2] = (sum2[o * 2] + temp) % mod;
        temp = temp2 = 0;
        
        ll k = K[o],b = tempp;
        temp = 2 * b * sum[o * 2 + 1] % mod;
        temp = (temp + 2 * k * inc[o * 2 + 1] % mod) % mod;
        temp = (temp + b * b % mod * len2 % mod) % mod;
        temp = (temp + k * b % mod * (len2 - 1) % mod * len2 % mod) % mod;
        temp2 = (len2 - 1) * len2 * (2 * len2 - 1) / 6 % mod;
        temp2 = temp2 * k % mod * k % mod;
        temp = (temp + temp2) % mod;
        sum2[o * 2 + 1] = (sum2[o * 2 + 1] + temp) % mod;
        temp = temp2 = 0;
        
        b = B[o],k = K[o];
        temp = b * len1 % mod;
        temp2 = (len1 - 1) * len1 / 2 % mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        sum[o * 2] = (sum[o * 2] + temp) % mod;
        temp = temp2 = 0;
        
        b = tempp,k = K[o];
        temp = b * len2 % mod;
        temp2 = (len2 - 1) * len2 / 2 % mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        sum[o * 2 + 1] = (sum[o * 2 + 1] + temp) % mod;
        temp = temp2 = 0;
        
        k = K[o],b = B[o];
        temp = (len1 - 1) * len1 / 2 % mod;
        temp = temp * b % mod;
        temp2 = (len1 - 1) * len1 * (2 * len1 - 1) / 6 % mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        inc[o * 2] = (inc[o * 2] + temp) % mod;
        temp = temp2 = 0;
        
        k = K[o],b = tempp;
        temp = (len2 - 1) * len2 / 2 % mod;
        temp = temp * b % mod;
        temp2 = (len2 - 1) * len2 * (2 * len2 - 1) / 6 % mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        inc[o * 2 + 1] = (inc[o * 2 + 1] + temp) % mod;
        temp = temp2 = 0; 
        B[o] = K[o] = 0; 
    }
}

void update3(ll cur,ll o,ll l,ll r,ll x,ll y,ll k,ll b)
{
    if (x <= l && r <= y)
    {
        b = b + (l - e[cur].l) * k % mod;
        b %= mod;
        cnt[o] = 1;
        K[o] += k;
        K[o] %= mod;
        B[o] += b;
        B[o] %= mod;
        ll temp = 2 * b * sum[o] % mod;
        temp = (temp + 2 * k * inc[o] % mod) % mod;
        temp = (temp + b * b % mod * (r - l + 1) % mod) % mod;
        temp = (temp + k * b % mod * (r - l) % mod * (r - l + 1) % mod) % mod;
        ll len = r - l + 1;
        ll temp2 = (len - 1) * len * (2 * len - 1) / 6;
        temp2 %= mod;
        ll temp3 = k * k % mod;
        temp = (temp + temp2 * temp3 % mod) % mod;
        sum2[o] += temp;
        sum2[o] %= mod;
        temp = b * len % mod;
        temp2 = (len - 1) * len / 2;
        temp2 %= mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        sum[o] += temp;
        sum[o] %= mod;
        temp2 = (len - 1) * len / 2;
        temp2 %= mod;
        temp2 = temp2 * b % mod;
        temp = temp2;
        temp2 = (len - 1) * len * (2 * len - 1) / 6;
        temp2 %= mod;
        temp2 = temp2 * k % mod;
        temp = (temp + temp2) % mod;
        inc[o] += temp;
        inc[o] %= mod;
        return;
    }
    pushdown3(o);
    ll mid = (l + r) >> 1;
    if (x <= mid)
        update3(cur,o * 2,l,mid,x,y,k,b);
    if (y > mid)
        update3(cur,o * 2 + 1,mid + 1,r,x,y,k,b);
    pushup(o);
}

ll query4(ll o,ll l,ll r,ll x,ll y)
{
    if (x <= l && r <= y)
    return sum[o];
    pushdown3(o);
    ll res = 0;
    ll mid = (l + r) >> 1;
    if (x <= mid)
        res += query4(o * 2,l,mid,x,y);
    res %= mod;
    if (y > mid)
        res += query4(o * 2 + 1,mid + 1,r,x,y);
    res %= mod;
    return res;
}

ll query5(ll o,ll l,ll r,ll x,ll y)
{
    if (x <= l && r <= y)
    return sum2[o];
    pushdown3(o);
    ll res = 0;
    ll mid = (l + r) >> 1;
    if (x <= mid)
    res += query5(o * 2,l,mid,x,y);
    res %= mod;
    if (y > mid)
    res += query5(o * 2 + 1,mid + 1,r,x,y);
    res %= mod;
    return res;
}

void solve4()
{
    build(1,1,n);
    for (int i = 1; i <= q; i++)
    {
        if (e[i].id == 1)
            update3(i,1,1,n,e[i].l,e[i].r,e[i].k,e[i].b);
        else
            if (e[i].id == 2)
                printf("%lld\n",query4(1,1,n,e[i].l,e[i].r));
        else
            printf("%lld\n",query5(1,1,n,e[i].l,e[i].r));
    }
}

int main()
{
    scanf("%lld%lld",&n,&q);
    for (ll i = 1; i <= n; i++)
        scanf("%lld",&a[i]);
    for (ll i = 1; i <= q; i++)
    {
        char ch[2];
        scanf("%s",ch);
        if (ch[0] == 'A')
           {
                e[i].id = 1;
                scanf("%lld%lld%lld%lld",&e[i].l,&e[i].r,&e[i].k,&e[i].b);
                if (e[i].k != 0)
                    flag1 = false;
           }
        if (ch[0] == 'B')
        {
            e[i].id = 2;
            scanf("%lld%lld",&e[i].l,&e[i].r);
        }
        if (ch[0] == 'C')
        {
            e[i].id = 3;
            flag2 = false;
            scanf("%lld%lld",&e[i].l,&e[i].r);
        }
    }
    if (n <= 1000 && q <= 1000)
       solve1();
    else
        if (flag1 && flag2)
            solve2();
    else
        if (flag1)
            solve2();
    else
        if (flag2)
        solve3();
    else
        solve4();
    
    return 0;   
}

3. 路径

问题描述

实在不知道怎么编题面了,就写得直白一点吧。反正没几个人写得完三题,估计都看不到这里。

给出一个仙人掌图,求图中最长路的长度。

 

Emmm……还是稍微具体一点吧。

仙人掌图是指一个有N个点与M条边的无向图,点从1到N标号,每条边有各自的长度,图中可能存在若干个简单环,但是,每个点最多只会属于1个简单环路。简单环是指一个经过至少两个点、且不经过重复点的环。(这里仙人掌图的定义也许和你在别处见过的不太一样,请仔细审题)

例如,图1所示的是一个仙人掌图,但图2则不是,因为3号点同属于两个简单环。

 

图1

 

图2

给出一个仙人掌图,你需要求出图中的最长路的长度。最长路不能经过重复的点。例如,假设图中所有边长度都为1的话,图1中的仙人掌图的一条最长路为1-2-3-4-5-6,长度为5。

 

输入格式

       第一行包含1个整数Q,表示数据组数;

       每组数据的第一行2个整数N,M,表示仙人掌图的点数和边数;

       每组数据的接下来M行,每行3个正整数x,y,z,描述一条连接点x与点y,长度为z的边。

      

输出格式

       对于每组数据输出一行,每行包含一个整数,表示最长路径的长度。

 

样例输入

2

6 7

1 2 1

2 3 1

3 1 1

3 4 1

4 5 1

5 6 1

6 4 1

4 4

1 2 1

2 3 2

3 4 3

4 1 4

 

样例输出

5

9

 

数据规模

对于10%的数据,Q ≤ 5,n ≤ 10;

另有20%的数据,满足n=m+1;

另有20%的数据,满足n=m;

另有20%的数据,满足每个环上的点数≤ 20;

对于100%的数据,Q ≤ 1000, 所有测试点的n之和 ≤ 100,000,z≤ 1000。

分析:思路同:bzoj1023,只不过边权不再是1,并且也没有了“距离”的概念,那么所有地方直接取max就好了,不必在环上走过的长度取min.环上的部分特别容易写错,需要引起注意.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 300010;

int Q,n,m,head[maxn],to[maxn],w[maxn],tot = 1,nextt[maxn];
int deep[maxn],pos[maxn],pre[maxn],low[maxn],dfs_clock;
int ans,f[maxn],fa[maxn],cnt,a[maxn],sum[maxn],l,r;

void init()
{
    memset(head,0,sizeof(head));
    tot = 1;
    memset(deep,0,sizeof(deep));
    memset(pre,0,sizeof(pre));
    memset(low,0,sizeof(low));
    dfs_clock = 0;
    ans = 0;
    memset(f,0,sizeof(f));
    memset(fa,0,sizeof(fa));
    cnt = 0;
    l = r = 1;
}

void add(int x,int y,int z)
{
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

void solve(int x,int y)
{
    memset(a,0,sizeof(a));
    memset(pos,0,sizeof(pos));
    cnt = deep[y] - deep[x] + 1;
    for (int i = y; i != x; i = fa[i])
        a[cnt--] = i;
    a[cnt] = x;
    cnt = deep[y] - deep[x] + 1;
    for (int i = 1; i <= cnt; i++)
    {
        a[cnt + i] = a[i];
        for (int j = head[a[i]];j;j = nextt[j])
        {
            int v = to[j];
            if (v == a[i + 1])
            {
                sum[i + 1] = w[j];
                break;
            }
        }
    }
    for (int i = 2; i <= cnt; i++) //不能从1复制,因为上面在处理i=cnt的时候已经处理了sum[cnt + 1].
        sum[i + cnt] = sum[i];
    for (int i = 1; i <= cnt * 2; i++)
        sum[i] += sum[i - 1];
    l = 1,r = 0;
    for (int i = 1; i <= cnt * 2; i++)
    {
        while (l <= r && i - pos[l] >= cnt)
            l++;
        ans = max(ans,f[a[pos[l]]] + f[a[i]] + sum[i] - sum[pos[l]]);
        while (l <= r && f[a[pos[r]]] - sum[pos[r]] <= f[a[i]] - sum[i])
            r--;
        pos[++r] = i;
    }
    for (int i = 2; i <= cnt; i++)
        f[x] = max(f[x],f[a[i]] + max(sum[i],sum[cnt + 1] - sum[i]));
}

void dfs(int u,int faa)
{
    pre[u] = low[u] = ++dfs_clock;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (v == faa)
            continue;
        if (!pre[v])
        {
            deep[v] = deep[u] + 1;
            fa[v] = u;
            dfs(v,u);
            low[u] = min(low[u],low[v]);
        }
        else
            low[u] = min(low[u],pre[v]);
        if (pre[u] < low[v])
        {
            ans = max(ans,f[u] + f[v] + w[i]);
            f[u] = max(f[u],f[v] + w[i]);
        }
    }
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (v == faa)
            continue;
        if (fa[v] != u && pre[u] < pre[v])
            solve(u,v);
    }
}

int main()
{
    scanf("%d",&Q);
    while (Q--)
    {
        init();
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= m; i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        dfs(1,0);
        printf("%d\n",ans);
    }

    return 0;
}

最初的暴力还写错了:

void dfs(int u,int dis)
{
    ans = max(ans,dis);
    vis[u] = 1;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
            dfs(v,dis + w[i]);
    }
}

如果改成下面这样就对了:

void dfs(int u,int dis)
{
    ans = max(ans,dis);
    vis[u] = 1;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
            dfs(v,dis + w[i]);
    }
    vis[u] = 0;  
}

vis数组没有清零,u这个点可能会有多条路径经过.最后写急了没有考虑到这一点.

教训:

  1.把握好时间,给后面的题目留充足的时间,对于一些难写的题目,要预估写好的时间.

  2.不要不敢写正解,有时候正解比暴力还要容易.

  3.代码习惯:如果一个地方可能会爆int,那么在内存不是很紧的情况下,所有变量用long long.

  4.表达式拆开来写.

  5.学会造大数据来对拍,有时候小数据找不出错误来.

 

posted @ 2018-01-17 18:52  zbtrs  阅读(556)  评论(0编辑  收藏  举报