2015多校第三场总结

  1. 1001 Magician
    线段树。维护区间四个值:奇奇,奇偶,偶奇,偶偶。更新父区间的每个值需注意:可由左儿子单独更新,可由右儿子单独更新,可由左右儿子结合一起更新,三种情况。

    
    #define clr(A,x) memset(A,x,sizeof(A))
    
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> P;
    const int mod = 1000000007;
    const int mm = 100005;
    const int inf =  1000000007;
    int A[mm];
    struct segment{
        LL sum[mm<<2][4];
        void pushup(LL a[],LL b[],LL c[])
        {
            for(int i = 0; i < 2; i++)
                for(int j = 0; j < 2; j++)
                a[i<<1|j] = max(b[i<<1|j],c[i<<1|j]);
            for(int i = 0; i < 2; i++)
            for(int j = 0; j < 2; j++)
            for(int k = 0; k < 2; k++)
            a[i<<1|j] = max(a[i<<1|j],b[i<<1|k]+c[(k^1)<<1|j]);
        }
    
        void build(int rt,int l,int r)
        {
            if(l==r){
                for(int i = 0; i < 4; i++) sum[rt][i] = -inf;
                if(l&1) sum[rt][3] = A[l];
                else sum[rt][0] = A[l];
            }else{
                int mid = (l+r)>>1;
                build(rt<<1,l,mid);
                build(rt<<1|1,mid+1,r);
                pushup(sum[rt],sum[rt<<1],sum[rt<<1|1]);
            }
        }
    
        void upd(int rt,int l,int r,int p,int x)
        {
            if(p < l || p > r) return ;
            if(l==p && p == r)
            {
                for(int i = 0; i < 4; i++) sum[rt][i] = -inf;
                if(l&1) sum[rt][3] = x;
                else sum[rt][0] = x;
            }else{
                int mid = (l+r)>>1;
                upd(rt<<1,l,mid,p,x);
                upd(rt<<1|1,mid+1,r,p,x);
                pushup(sum[rt],sum[rt<<1],sum[rt<<1|1]);
            }
        }
    
        void query(int rt,int l,int r,int L,int R,LL *s){
            if(r < L || R < l) return ;
            if(L <= l && r <= R){
                for(int i = 0; i < 4; i++) s[i] = sum[rt][i];
                return;
            }
            int mid = (l+r)>>1;
            if( R <= mid) { query(rt<<1,l,mid,L,R,s);return;}
            if( L > mid)  { query(rt<<1|1,mid+1,r,L,R,s);return;}
            LL ls[4] = {-inf,-inf,-inf,-inf};
            LL rs[4] = {-inf,-inf,-inf,-inf};
            query(rt<<1,l,mid,L,R,ls);
            query(rt<<1|1,mid+1,r,L,R,rs);
            pushup(s,ls,rs);
        }
    }seg;
    
    int n,m;
    int main() {
    //    freopen("wcbao.in","r",stdin);
    //    freopen("1012.in","r",stdin);
    //    freopen("out.txt","w",stdout);
        int T;
        cin >> T;
        while(T--)
        {
            cin >> n >> m;
            for(int i = 1; i <= n; i++)
                scanf("%d",A+i);
            int root = 1;
            seg.build(root,1,n);
            while(m--)
            {
                int op,x,y;
                scanf("%d%d%d",&op,&x,&y);
                if(op==0){
                    LL s[4] = {-inf,-inf,-inf,-inf};
                    seg.query(root,1,n,x,y,s);
                    LL ans = -inf;
                    for(int i = 0; i < 4; i++) ans = max(ans,s[i]);
                    printf("%I64d\n",ans);
                }else{
                    seg.upd(root,1,n,x,y);
                }
            }
        }
        return 0;
    }
  2. 1005 Fan Li

    • gcd(i,j)的所有情况可由O(nlogn)求得。
    • 对于最多区间数,用l[i]表示n==i时的最多区间数量,并且i是最后一个区间的右端点,用sum[i]表示l[i]对应的方案数。则,i由{j| l[i] = l[j]+1, j < i}更新。
    • 考虑gcd=x对答案的贡献值
  3. 1006 Beautiful Set
    计数问题,莫比乌斯反演。描述看似复杂,其实就是改写 ifi
    大致特点:

    • 所有可能情况 coni
    • 对于 coni ans += f(coni)
      考虑每个小部分对总体的贡献
      如:

    Mr. Hdu defines the beautiful value like this:
    For k from 1 to n, choose k numbers of the set, and calculate the gcd of the k numbers. The beautiful value of the k numbers is k * (gcd of the k chosen numbers). The beautiful value of the set is the sum of all of the beautifu
    l value of k numbers.

    于是
    ans = ni=1s[1,n],|s|=igcd(s)
    考虑每个gcd对答案的贡献,得到
    ans = i=1s[1,n]gcd(s)=i
    对于gcd=i,统计有多少个集合的gcd为i。从而可以用莫比乌斯解决。

  4. 1005 Hope
    NTT。Mod,N, g。N是2的幂,N | Mod-1,g是Mod原根。
    单位根 wn = gMod1N
    读题要细致。理解题意,搞清数据范围。
    递推式比较简单,

    dp[i]=j=1iC(i1,j1)(j1)!dp[ij]jj

    =j=1i(i1)!dp[ij]/(ij)!jj

    注意到这是一个卷积。但是每次要用到dp[0,i-1]来更新自己。
    我们从递推的角度考虑,我们计算出dp[i]的时候,用dp[i]更新dp[j],j>i。
    借鉴cdq分治的思想,用solve(l,r)表示用来求dp[l,r)。我们先求solve(l,(l+r)>>1),然后,我们计算dp[l,(l+r)>>1)对dp[l+r)>>1,r)的贡献。然后,solve((l+r)>>1,r)。
    cdq分治,从左向右依次求得结果。先求左儿子,计算左儿子对右儿子的贡献,求右儿子。
    不得不说,cdq这个方法太牛了!

    
    #include <bits/stdc++.h>
    
    
    #define clr(A,x) memset(A,x,sizeof(A))
    
    
    using namespace std;
    
    typedef long long LL;
    typedef pair<int,int> P;
    const int mm = 400005;
    const int mod = 998244353;
    const int Mod = 998244353;
    const int g = 3;
    LL fac[mm],inv[mm];
    
    LL A[mm],B[mm];
    LL a[mm],b[mm];
    LL pw(LL x,int y){
        LL res = 1;
        for(; y > 0; y >>= 1){
            if(y&1) res = res *x % mod;
            x = x*x % mod;
        }
        return res;
    }
    void Rader(LL c[],int n)
    {
        int j = n >> 1;
        for(int i = 1; i < n - 1; i++)
        {
            if(i < j) swap(c[i],c[j]);
            int k = n >> 1;
            for(; j >= k; k >>= 1) j -= k;
            j |= k;
        }
    }
    
    void NTT(LL c[],int n,int I)
    {
        Rader(c,n);
        for(int L = 2; L <= n; L <<= 1)
        {
            LL wn = pw(g,(mod-1)/L);
            if(I == -1) wn = pw(wn,mod-2);
            for(int j = 0; j < n; j += L){
                LL w = 1;
                for(int k = j; k < j + L/2; k++)
                {
                    LL u = c[k], v = c[k+L/2];
                    c[k] = (u + w*v % mod) % mod;
                    c[k+L/2] = (u - w*v % mod) % mod;
                    w = w * wn % mod;
                }
            }
        }
        if(I == -1){
            int INV = pw(n,mod-2);
            for(int i = 0; i < n; i++) c[i] = c[i]*INV % mod;
        }
    }
    
    void solve(int l,int r)
    {
        if(l == r) return ;
        int mid = (l+r)>>1;
        solve(l,mid);
        int len = r-l+1, n = 1;
        while(n < len+len) n <<= 1;
        for(int i = 0; i <= n+2; i++) a[i] = b[i] = 0;
        for(int i = 1; i <= r-l; i++) a[i] = 1ll*i*i % mod;
        for(int i = l; i <= mid; i++) b[i-l] = B[i];
    
        NTT(a,n,1);NTT(b,n,1);
        for(int i = 0; i < n; i++)
            b[i] = a[i] * b[i] % mod;
        NTT(b,n,-1);
        for(int i = mid+1; i <= r; i++)
        B[i] = (B[i] + b[i-l]*inv[i] % mod) % mod;
        solve(mid+1,r);
    }
    
    void init(int n)
    {
        fac[0] = inv[0] = 1;
        for(int i = 1; i <= n; i++){
            fac[i] = fac[i-1]*i % mod;
            inv[i] = pw(i,mod-2);
        }
        clr(B,0);
        B[0] = 1;
        solve(0,n);
    }
    
    int main()
    {
    //    freopen("wcbao.in","r",stdin);
    //    freopen("wcbao.out","w",stdout);
        int n = 100000;
        init(n);
        for(int i = 0; i <= n; i++)
            B[i] = (B[i]*fac[i] % mod + mod ) % mod;
        while(cin >> n)
        {
            cout << B[n] << endl;
        }
        return 0;
    }
  5. 1009 Boring Classes
    经典的三维偏序,输出字典序最小的方案。
    cdq分治。从右向左回溯答案得到一个DAG。字典序最小就是在这个DAG里找一条最长且字典序最小的路径。在这个DAG里对每个v求一个f[v],表示从v出发的最长路径。然后从左向右贪心求方案。
    cdq分治的时候从右向左做可以省掉DAG过程。

    
    #define PI acos(-1)
    
    
    #define clr(A,x) memset(A,x,sizeof(A))
    
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> P;
    typedef pair<int,P> pp;
    typedef complex<double> cpx;
    const int mm = 100005;
    const int mod = 1000000007;
    const int inf = 1000000007;
    int n;
    int L[mm],R[mm],len[mm];
    int p[mm],prv[mm];
    int cmp(int i,int j){
        return L[i] < L[j] || (L[i] == L[j] && i > j);
    }
    vector<int> dis;
    void discrete(int a[],int b[],int n)
    {
        dis.clear();
        for(int i = 0; i < n; i++)
            dis.push_back(a[i]);
        sort(dis.begin(),dis.end());
        dis.erase(unique(dis.begin(),dis.end()),dis.end());
        for(int i = 0; i < n; i++)
            b[i] = lower_bound(dis.begin(),dis.end(),a[i]) - dis.begin() + 1;
    }
    
    int C[mm];
    int Max(int x,int y){
        if(len[x] != len[y]) return len[x] > len[y] ? x : y;
        return x < y ? x : y;
    }
    
    void modify(int c[],int x,int id,int N){
        int dx = len[id];
        for( ; x < N; x += x&-x)
    //        c[x] = max(c[x],dx);
            c[x] = Max(c[x],id);
    }
    
    int getMax(int c[],int x){
        int res = c[x];
        for( ; x > 0; x -= x&-x)
    //        res = max(res,c[x]);
            res = Max(res,c[x]);
        return res;
    }
    
    int b[mm];
    
    void solve(int l,int r){
        if(l == r){
            len[l] = max(len[l],1);
            return ;
        }
        int mid = (l+r)>>1;
        solve(mid+1,r);
        for(int i = l; i <= r; i++) p[i] = i;
        sort(p+l,p+r+1,cmp);
        for(int i = 0; i <= r-l+1; i++) C[i] = 0;
        discrete(R+l,b+l,r-l+1);
        int N = r - l + 3;
        for(int i = l; i <= r; i++){
            int &id = p[i];
            if(id > mid){
                modify(C,b[id],id,N);
            }else{
                int x = getMax(C,b[id]);
    //            len[id] = max(x+1,len[id]);
                if(len[id] <= len[x]+1){
                    len[id] = len[x] + 1;
                    prv[id] = x;
                }
            }
        }
        solve(l,mid);
    }
    
    int main()
    {
    //    freopen("wcbao.in","r",stdin);
        while(cin >> n)
        {
            for(int i = 1; i <= n; i++) {
                scanf("%d",L+i);
            }
            for(int i = 1; i <= n; i++) {
                scanf("%d",R+i);
                R[i] = inf - R[i];
            }
            discrete(R+1,R+1,n);
            clr(len,0);clr(prv,0);
            solve(1,n);
            int s = 1;
            for(int i = 1; i <= n; i++)
            if(len[i] > len[s]) s = i;
            int ans = len[s];
            cout << ans << endl;
    //        for(int i = 1; i <= n; i++)
    //            printf("%d%c",len[i],i==n?'\n':' ');
            for(int i = 0; i < ans; i++)
            {
                printf("%d%c",s,i==ans-1?'\n':' ');
                s = prv[s];
            }
        }
        return 0;
    }
  6. 1010 Crazy Bobo
    博神A的题。想想当时自己还真是蠢,幸亏博神机智。就是找一棵子树,从该子树的根到该子树的任意节点都是递增序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值