NOIP多校联考13

问题 A: 【2022暑期集训8月20日】隔离

差点因为check传参传入了int而以为自己二分又写错了……

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1e5 + 10;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, m;
ll l, r;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node
{
    ll a, b;
    bool operator < (const node &T) const 
    {
        return a < T.a;
    }
}p[maxn];

bool check(ll d)
{
    ll pos = p[1].a + d;
    //printf("pos = %lld\n", pos);
    int num = 1, i = 1;
    while(pos <= p[m].b)
    {
        while(pos > p[i].b)
        {
            i++; 
        }
        pos = max(pos, p[i].a);
        //printf("pos = %lld\n", pos);
        num++; pos += d;
        //printf("pos = %lld num = %d\n", pos, num);
        if(num >= n) return 1;
    }
    if(num < n) return 0;
    else return 1;
}

int main()
{
    //freopen("1.in", "r", stdin);
    
    n = read(); m = read();
    for(int i=1; i<=m; i++)
    {
        scanf("%lld%lld", &p[i].a, &p[i].b);
    }
    sort(p+1, p+1+m);
    r = (p[m].b + 1) / n;
    //printf("%lld %d\n", p[m].b, n);
    //printf("r = %lld\n", r);
    while(l < r)
    {
        ll mid = (l + r + 1) >> 1;
        //printf("mid = %lld\n", mid);
        if(check(mid)) 
        {
            l = mid; //printf("mid = %lld\n", mid);
        }
        else r = mid - 1;
    }
    printf("%lld\n", l);
  
    return 0;
}
View Code

 

问题 B: 【2022暑期集训8月20日】绽放的花火

赛时对着一个f[i-1]=p*(f[i]+1)+(1-p)*(f[i-2]+1)表示无奈(先把f[i-2]提到了前面,但是发现我只知道f[n]=0不是道f[n-1]是多少,转化失败),原来可以高斯消元……

不过高斯消元太麻烦了,有一种更简单的思路,就是从当前位置走到下一个位置,一维从1循环到n1-1,二维就从1循环到n1+n2-1,就是不管是几维空间,有效的移动是特定有限的,当然从0开始循环也可以,把结束位置也改小一点就好了,这个都一样。

把f[i] = p + (1-p)*(f[i-1]+f[i]+1)化简就好了。

and:我才知道有了p%mod,(1-p)%mod可以直接算——

caorong告诉我的时候我还不信,非要自己验证一下……

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1e6 + 10;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

ll f[maxn], sum, ans, p, p1, p2;
int t, k;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

int main()
{
    //freopen("1.in", "r", stdin);
    
    t = read();
    while(t--)
    {
        k = read();
        sum = 0; ans = 0;
        for(int i=1; i<=k; i++)
        {
            int x = read();
            sum = (sum + x) % mod;
        }
        p = read();
        p1 = ((1-p)%mod+mod)%mod;
        p2 = qpow(p, mod-2);
        f[0] = 1;
        for(int i=1; i<sum-k; i++)
        {
            f[i] = (p1*f[i-1]%mod*p2%mod+p2)%mod;
        }
        for(int i=0; i<sum-k; i++)
        {
            ans = (ans + f[i]) % mod;
        }
        printf("%lld\n", ans);
    }
    exit(0);
  
    return 0;
}
View Code

 

问题 C: 【2022暑期集训8月20日】美化数列

40分的话据说直接暴力就行,我还开了个树状数组……说起来,算时间的话可能还比不上暴力呢……

啊对,关于等差数列什么的,我本来想用树状数组二次差分,结果发现这两个差值在其他种类的修改时没法维护,所以每次1询问两次循环求差值?得了吧还不如直接暴力地单点修改呢……

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1e5 + 10;
const int N = 1e6 + 2;
const ll mod = 19260817;

int n, m, D;
ll fib[maxn], c[maxn], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline int lowbit(int x)
{
    return x & -x;
}

inline void add(int x, int val)
{
    while(x <= n)
    {
        c[x] = (c[x] + val) % mod;
        x += lowbit(x);
    }
}

inline ll query(int x)
{
    ll sum = 0;
    while(x)
    {
        sum = (sum + c[x]) % mod;
        x -= lowbit(x);
    }
    return sum;
}

int main()
{
    //freopen("1.in", "r", stdin);
    //freopen("ww.txt", "w", stdout);
    
    n = read(); m = read(); D = read();
    for(int i=1; i<=n; i++)
    {
        int x = read(); add(i, x);
    }
    fib[1] = 1; fib[2] = 1;
    for(int i=3; i<=n; i++)
    {
        fib[i] = (fib[i-1] + fib[i-2]) % mod;
    }
    while(m--)
    {
        int tp = read(), l = read(), r = read();
        if(tp == 1)
        {
            ll k = read(), d = read();
            for(int i=l; i<=r; i++)
            {
                add(i, k);
                k = (k + d) % mod;
            }
        }
        else if(tp == 2)
        {
            ll k = read();
            for(int i=l; i<=r; i++)
            {
                add(i, k);
                k = k * D % mod;
            }
        }
        else if(tp == 3)
        {
            for(int i=l; i<=r; i++)
            {
                add(i, fib[i-l+1]);
            }
        }
        else 
        {
            ans = (query(r) - query(l-1) + mod) % mod;
            printf("%lld\n", ans);
        }
    }
  
    return 0;
}
傻乎乎的40

正解: 对于等差数列,区间求和可以变成[(首项+末项)/2],记录首项和长度就可以更新,小区间的首项可以由大区间的首项推出,对于等比数列,可以预处理首项为1的单项数值+前缀和,首项改变并不是全部改变,其实是每一项多乘了一个k,根据乘法结合律可以把它提出来,所以等比数列的首项可以合并。

最有趣的是fibo的处理,关于这两个系数,其实就是f[3] = f[2]+f[1],f[4] = f[3]+f[2] = f[2]+f[1]+f[2],所以f[2]的系数是2, f[1]的系数是1……所以说只要知道前两项,就可以得到后面的任意一项和任意前多少项的和(两个系数的前缀和+乘法分配率)。

这个结论可以拓展:不只是用f[1]和f[2]表示其它的,任意两个连续的斐波那契数都可以沿用预处理的系数,只要每一项都是前两项相加得到的就可以,当然系数的起始位置需要作出相应的变化。这也可以解释为什么修改时记录的前两个斐波那契数可以直接累加。

所以三种情况的处理原理很相似,三种情况的操作步骤也很相似:先由大区间的左端点的值得出小区间左端点的值,再由它得到区间修改的贡献和计入答案。


#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1e5 + 10;
const int N = 1e6 + 2;
const ll mod = 19260817;

#define int ll
int n, m;
ll D, f1[maxn], f2[maxn], sumf1[maxn], sumf2[maxn], g[maxn], sumg[maxn];
ll k1[maxn<<2], d1[maxn<<2], k2[maxn<<2], h1[maxn<<2], h2[maxn<<2];

struct node 
{
    bool lazy1, lazy2, lazy3;
    ll sum;
}t[maxn<<2];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void Fibo()
{
    f1[1] = 1; f2[1] = 0;
    f1[2] = 0; f2[2] = 1;
    for(int i=3; i<=n; i++)
    {
        f1[i] = (f1[i-1]+f1[i-2])%mod; f2[i] = (f2[i-1]+f2[i-2])%mod;
    }
    for(int i=1; i<=n; i++)
    {
        sumf1[i] = (sumf1[i-1]+f1[i])%mod; sumf2[i] = (sumf2[i-1]+f2[i])%mod;
    }
    g[1] = 1;
    for(int i=2; i<=n; i++) g[i] = g[i-1]*D%mod;
    for(int i=1; i<=n; i++) sumg[i] = (sumg[i-1]+g[i])%mod;
}

inline ll calc(ll k, ll len, ll d)
{
    ll res = k + k + (len-1)*d;
    res = res*len/2%mod;
    return res;
}

inline void pushup(int x)
{
    t[x].sum = (t[x<<1].sum+t[x<<1|1].sum)%mod;
}

inline void add1(int x, int l, int r, int L, int R, ll k, ll d)
{
    k += (l-L)*d;
    t[x].sum += calc(k, r-l+1, d);
    t[x].sum %= mod;
    k1[x] += k; d1[x] += d;
    t[x].lazy1 = 1;
}

inline void add2(int x, int l, int r, int L, int R, ll k)
{
    k = k*g[l-L+1]%mod;
    t[x].sum += sumg[r-l+1]*k%mod;
    t[x].sum %= mod;
    k2[x] += k;
    t[x].lazy2 = 1;
}

inline void add3(int x, int l, int r, int L, int R, ll a, ll b)
{
    if(l == r) 
    {
        t[x].sum += f1[l-L+1]*a%mod+f2[l-L+1]*b%mod;
        t[x].sum %= mod;
        return;
    }
    ll res1 = f1[l-L+1]*a%mod+f2[l-L+1]*b%mod;
    ll res2 = f1[l-L+2]*a%mod+f2[l-L+2]*b%mod;
    t[x].sum += res1*sumf1[r-l+1]%mod+res2*sumf2[r-l+1]%mod;
    t[x].sum %= mod;
    h1[x] += res1, h1[x] %= mod;
    h2[x] += res2, h2[x] %= mod;
    t[x].lazy3 = 1;
}

inline void pushdown1(int x, int l, int r)
{
    int mid = (l + r) >> 1;
    add1(x<<1, l, mid, l, r, k1[x], d1[x]);
    add1(x<<1|1, mid+1, r, l, r, k1[x], d1[x]);
    k1[x] = d1[x] = 0;
    t[x].lazy1 = 0;
}

inline void pushdown2(int x, int l, int r)
{
    int mid = (l + r) >> 1;
    add2(x<<1, l, mid, l, r, k2[x]);
    add2(x<<1|1, mid+1, r, l, r, k2[x]);
    k2[x] = 0;
    t[x].lazy2 = 0;
}

inline void pushdown3(int x, int l, int r)
{
    int mid = (l + r) >> 1;
    add3(x<<1, l, mid, l, r, h1[x], h2[x]);
    add3(x<<1|1, mid+1, r, l, r, h1[x], h2[x]);
    h1[x] = h2[x] = 0;
    t[x].lazy3 = 0;
}

void build(int x, int l, int r)
{
    if(l == r)
    {
        t[x].sum = read()%mod;
        return;
    }
    int mid = (l + r) >> 1;
    build(x<<1, l, mid);
    build(x<<1|1, mid+1, r);
    pushup(x);
}

void update1(int x, int l, int r, int L, int R, ll k, ll d)
{
    if(L <= l && r <= R)
    {
        add1(x, l, r, L, R, k, d);
        return;
    }
    if(t[x].lazy1) pushdown1(x, l, r);
    if(t[x].lazy2) pushdown2(x, l, r);
    if(t[x].lazy3) pushdown3(x, l, r);

    int mid = (l + r) >> 1;
    if(L <= mid) update1(x<<1, l, mid, L, R, k, d);
    if(R > mid) update1(x<<1|1, mid+1, r, L, R, k, d);
    pushup(x);
}

void update2(int x, int l, int r, int L, int R, ll k)
{
    if(L <= l && r <= R)
    {
        add2(x, l, r, L, R, k);
        return;
    }
    if(t[x].lazy1) pushdown1(x, l, r);
    if(t[x].lazy2) pushdown2(x, l, r);
    if(t[x].lazy3) pushdown3(x, l, r);

    int mid = (l + r) >> 1;
    if(L <= mid) update2(x<<1, l, mid, L, R, k);
    if(R > mid) update2(x<<1|1, mid+1, r, L, R, k);
    pushup(x);
}

void update3(int x, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
    {
        add3(x, l, r, L, R, 1, 1);
        return;
    }
    if(t[x].lazy1) pushdown1(x, l, r);
    if(t[x].lazy2) pushdown2(x, l, r);
    if(t[x].lazy3) pushdown3(x, l, r);

    int mid = (l + r) >> 1;
    if(L <= mid) update3(x<<1, l, mid, L, R);
    if(R > mid) update3(x<<1|1, mid+1, r, L, R);
    pushup(x);
}

ll query(int x, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return t[x].sum;
    if(t[x].lazy1) pushdown1(x, l, r);
    if(t[x].lazy2) pushdown2(x, l, r);
    if(t[x].lazy3) pushdown3(x, l, r);

    int mid = (l + r) >> 1;
    if(R <= mid) return query(x<<1, l, mid, L, R);
    if(L > mid) return query(x<<1|1, mid+1, r, L, R);
    return (query(x<<1, l, mid, L, R)+query(x<<1|1, mid+1, r, L, R))%mod;
}

inline void solve1()
{
    int l = read(), r = read(); ll k = read(), d = read();
    update1(1, 1, n, l, r, k, d);
}

inline void solve2()
{
    int l = read(), r = read(); ll k = read();
    update2(1, 1, n, l, r, k);
}

inline void solve3()
{
    int l = read(), r = read();
    update3(1, 1, n, l, r);
}

inline void solve4()
{
    int l = read(), r = read();
    ll ans = query(1, 1, n, l, r);
    ans = (ans%mod+mod)%mod;
    printf("%lld\n", ans);
}

signed main()
{
    //freopen("1.in", "r", stdin);
    
    n = read(), m = read(), D = read();
    Fibo();
    build(1, 1, n);
    while(m--)
    {
        int tp = read();
        if(tp == 1) solve1();
        else if(tp == 2) solve2();
        else if(tp == 3) solve3();
        else solve4();
    }
  
    return 0;
}
%%%Sakura

 

问题 D: 【2022暑期集训8月20日】ZB的旋转树

本来打算根据树的中序遍历生成先序遍历结合起来凑成合法的树(旋转的顺序问题),然后枚举计算。。但是开始写发现不少细节都不会,而且那个数据范围好像也不打算给我这种想dfs生成排列的考生骗分的机会。

剩下好长时间不知道干嘛,于是就开始默写网络流的板子*n

posted @ 2022-08-20 16:33  Catherine_leah  阅读(34)  评论(0编辑  收藏  举报
/* */