冷滟泽的个人博客冷滟泽的个人博客

20190909

A - Sasha and Array: CodeForces - 718C

题意

维护一个序列 A ,支持两种操作:

  • 区间加上一个正整数
  • 查询 \sum_{i=l}^{r} f(a_i)f(i) 表示斐波那契数列的第 i

思路

线段树维护矩阵 \begin{pmatrix}1 & 1\\ 1 & 0\end{pmatrix} 的次幂,区间加操作即为乘上矩阵的若干次幂。由于矩阵乘法满足结合律和分配律,所以可以维护区间和。

代码

#pragma GCC optimize(3, "Ofast", "inline")
#include <cstdio>
#include <cstring>
#include <cctype>
#include <tr1/unordered_map>
using namespace std;
typedef long long ll;
const int MAXN=110000;
const int MAXB=100;
const int MOD=1E9+7;
struct Matrix
{
    int n, m, a[5][5];
    Matrix operator + (const Matrix& b)
    {
        Matrix c(n, m);
        for (int i=0; i<n; i++)
            for (int j=0; j<m; j++)
                c.a[i][j]=(a[i][j]+b.a[i][j])%MOD;
        return c;
    }
    Matrix operator * (const Matrix& b)
    {
        Matrix c(n, b.m);
        for (int i=0; i<n; i++)
            for (int j=0; j<b.m; j++)
                for (int k=0; k<m; k++)
                    c.a[i][j]=(c.a[i][j]+1ll*a[i][k]*b.a[k][j])%MOD;
        return c;
    }
    bool isid()
    {
        for (int i=0; i<n; i++)
            for (int j=0; j<m; j++)
                if (i==j&&a[i][j]==0||i!=j&&a[i][j]!=0) return 0;
        return 1;
    }
    Matrix(int r=0, int c=0, bool id=0)
    {
        n=r, m=c;
        memset(a, 0, sizeof a);
        if (id) for (int i=0; i<r; i++) a[i][i]=1;
    }
} b[MAXB];
void prework()
{
    b[0]=Matrix(2, 2);
    b[0].a[0][0]=1; b[0].a[0][1]=1; b[0].a[1][0]=1;
    for (int i=1; i<MAXB; i++)
        b[i]=b[i-1]*b[i-1];
}
Matrix qpow(ll k)
{
    Matrix s(2, 2, 1);
    for (int t=0; k; t++, k>>=1)
        if (k&1) s=s*b[t];
    return s;
}
struct SegmentTree
{
    struct Node
    {
        Matrix val, mul;
    } tr[4*MAXN];
    #define lc (o<<1)
    #define rc (o<<1|1)
    inline void pushup(int o)
    {
        tr[o].val=tr[lc].val+tr[rc].val;
    }
    inline void mul(int o, const Matrix& k)
    {
        tr[o].val=tr[o].val*k;
        tr[o].mul=tr[o].mul*k;
    }
    inline void pushdown(int o)
    {
        if (!tr[o].mul.isid())
        {
            mul(lc, tr[o].mul);
            mul(rc, tr[o].mul);
            tr[o].mul=Matrix(2, 2, 1);
        }
    }
    void build(int o, int l, int r, int* a)
    {
        tr[o].mul=Matrix(2, 2, 1);
        if (l==r) { tr[o].val=qpow(a[l]-1); return; }
        int mid=l+r>>1;
        build(lc, l, mid, a);
        build(rc, mid+1, r, a);
        pushup(o);
    }
    void update(int o, int l, int r, int ul, int ur, const Matrix& k)
    {
        if (l>ur||r<ul) return;
        if (ul<=l&&r<=ur) { mul(o, k); return; }
        pushdown(o); int mid=l+r>>1;
        update(lc, l, mid, ul, ur, k);
        update(rc, mid+1, r, ul, ur, k);
        pushup(o);
    }
    int query(int o, int l, int r, int ql, int qr)
    {
        if (l>qr||r<ql) return 0;
        if (ql<=l&&r<=qr) return tr[o].val.a[0][0];
        pushdown(o); int mid=l+r>>1;
        return (query(lc, l, mid, ql, qr)
               +query(rc, mid+1, r, ql, qr))%MOD;
    }
    #undef lc
    #undef rc
} st;
int a[MAXN];
inline int read()
{
    int x=0; char ch=0;
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
    return x;
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
int main()
{
//  freopen("A.in", "r", stdin);
//  freopen("A.out", "w", stdout);
    int n=read(), m=read();
    for (int i=1; i<=n; i++) a[i]=read();
    prework();
    st.build(1, 1, n, a);
    while (m--)
    {
        int op=read(), l=read(), r=read(), x;
        if (op==1) x=read(), st.update(1, 1, n, l, r, qpow(x));
        else write(st.query(1, 1, n, l, r)), putchar('\n');
    }
    return 0;
}

B - 回转寿司: HYSBZ - 4627

题意

在一个序列中选择一段区间,使这段区间中元素之和在 [l,r] 中。求一共有多少个这样的区间。

思路

枚举右端点,用平衡树维护以它右端点的区间和,右端点向右移动相当于整体加操作,统计答案即统计有多少值在 [l,r] 中的元素。

代码

#include <cstdio>
#include <cstdlib>
typedef long long ll;
const int MAXN=110000;
struct Treap
{
    struct Node
    {
        int ls, rs, wgt, siz;
        ll val, add;
    } tr[MAXN];
    int cnt, root;
    inline int newNode(int k)
    {
        int x=++cnt;
        tr[x].ls=tr[x].rs=0; tr[x].wgt=rand();
        tr[x].siz=1; tr[x].val=k; tr[x].add=0;
        return x;
    }
    inline void pushup(int x)
    {
        tr[x].siz=1;
        if (tr[x].ls) tr[x].siz+=tr[tr[x].ls].siz;
        if (tr[x].rs) tr[x].siz+=tr[tr[x].rs].siz;
    }
    inline void add(int x, int k)
    {
        if (x) tr[x].val+=k, tr[x].add+=k;
    }
    inline void pushdown(int x)
    {
        if (x&&tr[x].add)
        {
            if (tr[x].ls) add(tr[x].ls, tr[x].add);
            if (tr[x].rs) add(tr[x].rs, tr[x].add);
            tr[x].add=0;
        }
    }
    void split(int o, int& x, int& y, int k)
    {
        pushdown(o);
        if (!o) x=y=0;
        else if (tr[o].val<k)
            x=o, split(tr[o].rs, tr[x].rs, y, k), pushup(x);
        else y=o, split(tr[o].ls, x, tr[y].ls, k), pushup(y);
    }
    void merge(int x, int y, int& o)
    {
        pushdown(x); pushdown(y);
        if (!x||!y) o=x+y;
        else if (tr[x].wgt<tr[y].wgt)
            o=x, merge(tr[x].rs, y, tr[o].rs), pushup(o);
        else o=y, merge(x, tr[y].ls, tr[o].ls), pushup(o);
    }
    void insert(int k)
    {
        int x, y, z, w=newNode(k);
        split(root, x, y, k);
        merge(x, w, z);
        merge(z, y, root);
    }
    int query(int l, int r)
    {
        int x, y, z, w;
        split(root, x, y, l);
        split(y, z, w, r+1);
        int res=tr[z].siz;
        merge(x, z, y);
        merge(y, w, root);
        return res;
    }
} treap;
int main()
{
//  freopen("B.in", "r", stdin);
//  freopen("B.out", "w", stdout);
    int n, l, r;
    scanf("%d%d%d", &n, &l, &r);
    ll ans=0;
    for (int i=1; i<=n; i++)
    {
        int x; scanf("%d", &x);
        treap.add(treap.root, x);
        treap.insert(x);
        ans+=treap.query(l, r);
    }
    printf("%lld\n", ans);
    return 0;
}

C - 一次元リバーシ / 1D Reversi: AtCoder - 2146

题意

有一排白色或黑色的石头。每次操作你可以在左右两端加一颗这样的石头,并把它与离他最近的同色石头之间的石头都改为它的颜色。问最少的操作次数。

思路

观察样例不难发现,最少的操作次数就是前后两个颜色不同的个数。直接统计答案即可。

代码

#include <cstdio>
const int MAXN=110000;
char s[MAXN];
int main()
{
//  freopen("C.in", "r", stdin);
//  freopen("C.out", "w", stdout);
    int ans=0;
    scanf("%s", s);
    for (int i=1; s[i]; i++)
        if (s[i]!=s[i-1]) ans++;
    printf("%d\n", ans);
    return 0;
}

D - 高橋君と見えざる手 / An Invisible Hand [AtCoder - 2147]

题意

有一些城镇,从 1n 线性排列。高桥君从城镇 1 出发前往城镇 n,不能往回走,中途可以在城镇买入或卖出苹果最多共 t 个苹果。每个城镇买入或卖出苹果的价格为 a_i,且互不相同。你可以花 |a_i-a_i'| 的代价把某一城镇的 a_i 改为 a_i' 。求使高桥君获得利润的最大值变小的最小代价。

思路

无论 t 多少,高桥君都会在两个差价最大的城市买入和卖出苹果。于是统计这些城市有多少对,并把这些花 1 的代价把这些对拆掉,总代价就是对数。记录当前 a_i 最小值、当前差价最大值和当前最大值的对数,扫一遍统计即可。

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN=110000;
const int INF=0X3F3F3F3F;
int a[MAXN];
int main()
{
//  freopen("D.in", "r", stdin);
//  freopen("D.out", "w", stdout);
    int n, t;
    scanf("%d%d", &n, &t);
    for (int i=1; i<=n; i++) scanf("%d", &a[i]);
    int mx=0, ans=0;
    for (int i=1, mi=INF; i<=n; i++)
    {
        mi=min(mi, a[i]);
        if (a[i]-mi>mx) mx=a[i]-mi, ans=0;
        if (a[i]-mi==mx) ans++;
    }
    printf("%d\n", ans);
    return 0;
}

E - 木と整数 / Integers on a Tree: AtCoder - 2148

题意

有一个 N 个点的树,其中 K 个点已被赋值。现在要为其他点赋值,使得对于任何由一条边直接连接的两个顶点,这两点点权恰好相差 1 。 确定是否有合法方案。如果有,给出一个方案。

思路

按点权的奇偶性将点分为黑点和白点。一遍 DFS 对这棵树进行黑白染色,若矛盾则无解。再次 DFS ,对每个点维护一个区间集合表示它的取值范围,若一个点的儿子的集合为 [l_i,r_i] ,则该点的集合应该是 \bigcap [l_i-1,r_i+1] 。如果交集为空,则无解。最后再从上到下 DFS 一下,保证每个点的值都在其集合中即可输出一组可行解。

代码

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN=110000;
const int INF=1E7;
vector<int> g[MAXN];
int w[MAXN], l[MAXN], r[MAXN];
bool c[MAXN];
bool dfs1(int u, int f, bool t)
{
    c[u]=t;
    if (~w[u]&&w[u]%2!=t) return 0;
    for (int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i];
        if (v!=f&&!dfs1(v, u, t^1)) return 0;
    }
    return 1;
}
int dfs2(int u, int f)
{
    if (~w[u]) l[u]=r[u]=w[u];
    for (int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i];
        if (v==f) continue;
        if (!dfs2(v, u)) return 0;
        l[u]=max(l[u], l[v]-1);
        r[u]=min(r[u], r[v]+1);
    }
    return l[u]<=r[u];
}
void dfs3(int u, int f, int k)
{
    w[u]=k;
    for (int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i];
        if (v==f) continue;
        if (l[v]<=k-1&&k-1<=r[v]) dfs3(v, u, k-1);
        else dfs3(v, u, k+1);
    }
}
int main()
{
//  freopen("E.in", "r", stdin);
//  freopen("E.out", "w", stdout);
    int n, k;
    scanf("%d", &n);
    for (int i=1; i<n; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    scanf("%d", &k);
    memset(w, -1, sizeof w);
    for (int i=1; i<=k; i++)
    {
        int v, p;
        scanf("%d%d", &v, &p);
        w[v]=p;
    }
    for (int i=1; i<=n; i++)
        if (~w[i])
        {
            if (!dfs1(i, 0, w[i]%2))
            { puts("No"); return 0; }
            break;
        }
    for (int i=1; i<=n; i++) l[i]=-INF, r[i]=INF;
    if (!dfs2(1, 0)) { puts("No"); return 0; }
    puts("Yes");
    dfs3(1, 0, l[1]);
    for (int i=1; i<=n; i++) printf("%d\n", w[i]);
    return 0;
}

F - すぬけ君の塗り絵 2 / Snuke's Coloring 2: AtCoder - 2149

未经允许不得转载:冷滟泽的个人博客 » 20190909

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址