zoukankan      html  css  js  c++  java
  • HGOI 20191106 题解

    Problem A  旅行者

    有$n$种转移装置,每种转移装置本质相同,每种装置可以前进$a_i$单位,但只有$b_i$个。

    从初始坐标为$0$出发,途中不能经过$c_1,c2,...,c_m$中的任意一个点。

    走到$sumlimits_{i = 1}^n a_ib_i$位置的方案数$mod 10^9 + 7$的值。

    对于$100\%$的数据满足$1 leq n leq 6 , 1 leq m leq 10^5 ,0<c_i < sumlimits_{i = 1}^n a_ib_i$

      Solution : 

        由于每个装置本质相同,那么我们只需要记录当前使用的转移装置数作为状态即可。

        这样定义状态的总状态数时$prod_{i = 1}^n b_i leq 13^6 = 4826809$

        注意,由于有$m$点不能走,还需要开一个$hash$存当前值能不能走,特殊判掉即可。

        转移的时候枚举当前通过那个转移装置走到当前位置,转移时间复杂度为$O(n)$

        请注意,本题的模数为$10^8 + 7$,您是否数错了零?

        所以,本题的总时间复杂度是$O(nprodlimits_{i=1}^{n} b_i)$

    # pragma GCC optimize(3)
    # include<bits/stdc++.h>
    # define int long long
    # define hash Hash
    # define Rint register int
    using namespace std;
    const int mo=100000007;
    int f[13][13][13][13][13][13];
    struct rec{int a,b;}a[10];
    int n,m;
    vector<int>hash[10007];
    inline int read() {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    void insert(int key) {
        int k = key % 10007,sz = hash[k].size();
        for (int i=0;i<sz;i++) if (hash[k][i] == key) return;
        hash[k].push_back(key);
    }
    bool find(int key) {
        int k=key % 10007,sz = hash[k].size();
        for (int i=0;i<sz;i++) if (hash[k][i] == key) return true;
        return false;
    }
    signed main() {
        n=read();
        for (Rint i=1;i<=n;i++) {
            a[i].a=read(); a[i].b=read();
        }
        m=read();
        for (Rint i=1;i<=m;i++) {
            int t=read(); insert(t); 
        }
        f[0][0][0][0][0][0]=1;
        for (Rint a1=0;a1<=a[1].b;a1++) for (Rint a2=0;a2<=a[2].b;a2++)
        for (Rint a3=0;a3<=a[3].b;a3++) for (Rint a4=0;a4<=a[4].b;a4++)
        for (Rint a5=0;a5<=a[5].b;a5++) for (Rint a6=0;a6<=a[6].b;a6++) {
            int tmp = a1*a[1].a+a2*a[2].a+a3*a[3].a+a4*a[4].a+a5*a[5].a+a6*a[6].a;
            if (find(tmp)) {
                f[a1][a2][a3][a4][a5][a6]=0;
                continue;
            } 
            if (a1+1<=a[1].b) (f[a1+1][a2][a3][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
            if (a2+1<=a[2].b) (f[a1][a2+1][a3][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
            if (a3+1<=a[3].b) (f[a1][a2][a3+1][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
            if (a4+1<=a[4].b) (f[a1][a2][a3][a4+1][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
            if (a5+1<=a[5].b) (f[a1][a2][a3][a4][a5+1][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
            if (a6+1<=a[6].b) (f[a1][a2][a3][a4][a5][a6+1]+=f[a1][a2][a3][a4][a5][a6])%=mo; 
        }
        printf("%lld
    ",f[a[1].b][a[2].b][a[3].b][a[4].b][a[5].b][a[6].b]%mo);  
        return 0;
    }
    traveller.cpp

    Problem B 序列

    定义两个数$(x,y)$是好的,当且仅当$x leq y$且$x oplus y$二进制表示下含有奇数个$1$。

    现在给出$n$个区间$[l_i,r_i]$,对于每一个$iin[1,n]$,输出前$i$个区间并中好的数对的个数。

    即输出满足下列条件的$(x,y)$的对数。

    • $x,y incup_{j=1}^i [l_j,r_j]$
    • $xleq y$
    • $x oplus y$二进制表示下含有奇数个$1$。

    对于$100\%$的数据满足$1 leq n leq 10^5 , 1leq l_ileq r_ileq 2^{32}-1$

      Solution : 

    ​     首先,$x oplus y$的二进制表示下有奇数个$1$有充要条件:一个数有偶数个$1$,一个数有奇数个$1$ 。

    ​     设$x,y$按二进制位写开来,同$1$的数的对数为$w$,那么剩余的奇数个$1$和偶数个$1$都会对答案产生$1$的贡献。

    ​     如果我们要统计数集$S$中好的数对的个数,我们就只要数一数这个数集中有多少个数含有偶数个$1$,有多少数有奇数个一,即可。最后的答案就是他们两个之积。

    ​     如何求一个区间$[l,r]$内有多少个数含有奇数个$1$或者偶数个$1$呢。

    ​     我们只要求前缀和即可,问题转化为求$[1,x]$的答案。

    ​     观察到一对数$0,1 ; 2, 3 ; 4,5 ... $从$0$开始每两个数一组一定是一个数字含有奇数个$1$,另外一个数字含有偶数个$1$.

    ​     所以,若$x$是奇数,$[1,x]$的答案直接是$x/2$了; 否则还需要特殊判断$x$这个数字到底是含有奇数个$1$,另外一个数字含有偶数个$1$.

    ​     接下来的问题就转化为线段的并了,我们可以很方便的用主席树来解决。

    ​     对值域建动态开点的线段树,当前区间如果被覆盖了直接打上一个$vis$标记,下一次不覆盖当前区间即可。

    ​     设数的值域是$S$,那么这样做的时间复杂度是$O(n log_2S)$

    # pragma GCC optimize(3)
    # include <bits/stdc++.h>
    # define int long long
    # define lowbit(x) (x&(-x))
    const int N=1e5+10;
    using namespace std;
    int n,tot; 
    inline int read() {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    } 
    void write(int x) {
        if (x<0) x=-x,putchar('-');
        if (x>9) write(x/10);
        putchar('0'+x%10); 
    }
    int query(int x) {
        if (x&1) return x/2+1;
        int ret=0,res=x/2;
        while (x) { x-=lowbit(x); ret^=1;}
        return res+ret;
    }
    bool vis[N*32];
    struct Seg {
        int ls,rs,cnt1,cnt2;
    }tr[N*32];
    # define ls tr[x].ls
    # define rs tr[x].rs
    void update(int &x,int l,int r,int opl,int opr) {
        if (!x) x=++tot; if (vis[x]) return;
        if (opl<=l && r<=opr) {
            vis[x]=1;
            tr[x].cnt1=query(r)-query(l-1);
            tr[x].cnt2=r-l+1-tr[x].cnt1;
            return;
        }
        int mid=(l+r)>>1;
        if (opl<=mid) update(ls,l,mid,opl,opr);
        if (opr>mid) update(rs,mid+1,r,opl,opr);
        tr[x].cnt1=tr[ls].cnt1+tr[rs].cnt1;
        tr[x].cnt2=tr[ls].cnt2+tr[rs].cnt2;
    }
    signed main() {
        n=read();
        int root=0;
        for (int i=1;i<=n;i++) {
            int l=read(),r=read();
            update(root,1,(1ll<<63)-1,l,r);
            write(tr[1].cnt1*tr[1].cnt2);
            putchar('
    ');
        }
        return 0;
    }
    sequence.cpp

    Problem C 钢琴家

    给出一个基本字符串$S$,给出$n$个目标字符串集合$t_i$。

    要求修改一些$S_i$使得可以将$S$划分成一些子串,使得这些子串都在目标字符串集合中出现。

    让替换次数尽可能小,且保证存在至少一个最优解。

    对于$100\%$的数据,满足 $1 leq |t_i| leq |S|leq 10^3 , 1leq nleq 10^2​$

     ​ Solution :

      本题可以直接用$DP$求出最优策略,设$f[i]$表示匹配到长度$i$的最小代价。

    ​   枚举一个目标字符串$1 leq j leq n$,可以考虑$[i-|t_j|+1 , i]$用串$t_j$来修改。

    ​   然后计算这样做的代价,就是$sumlimits_{k = 1}^{|t_j|} [ S[i-|t_j|+k] eq t_j[k] ]$

    ​   记录每一次最优的转移是从何而来,这样可以输出方案。

    ​   最后直接按照最优方案输出即可。

    ​   时间复杂度为$O(n|S|^2)$

    # pragma GCC optimize(3)
    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e3+10;
    char t[N][N],s[N];
    int n,f[N],len[N];
    int pre[N];
    int ans[N];
    int main() {
        scanf("%s",s+1);int l=strlen(s+1);
        scanf("%d",&n);
        for (int i=1;i<=n;i++) {
            scanf("%s",t[i]+1); 
            len[i]=strlen(t[i]+1);
        }
        memset(f,0x3f,sizeof(f)); f[0]=0;
        for (int i=1;i<=l;i++) 
            for (int j=1;j<=n;j++) if (i>=len[j]){
                int res = 0;
                for (int k=1;k<=len[j];k++) {
                    if (t[j][k]!=s[i-len[j]+k]) res++;    
                }
                if (f[i-len[j]]+res<f[i]) {
                    f[i] = f[i-len[j]]+res;
                    pre[i] = j;  
                }
            }    
        int now = l;
        while (true) {
            if (now == 0) break;
            ans[++ans[0]] = pre[now];
            now=now-len[pre[now]];
        }
        for (int i=ans[0];i>=1;i--) {
            for (int j=1;j<=len[ans[i]];j++)
                putchar(t[ans[i]][j]);
            putchar('
    ');    
        }
        return 0;
    }
    pianist.cpp
  • 相关阅读:
    Crawling Computing Ranking 很长时间, 怎么办?
    明月外,净红尘
    数据库人员面试:SQL Server常用测试题
    华山人物志——苏颖超
    安全性和 XML Web services
    聚簇索引与非聚簇索引的区别以及SQL Server查询优化技术
    SQL2000里的数据类型
    [精华] 数据库的查询优化技术
    深入浅出理解索引结构
    XSL
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11804482.html
Copyright © 2011-2022 走看看