zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第五场)题解

    Name Date Rank Solved A B C D E F G H I J K
    2020 Multi-University,Nowcoder Day 5 2020.07.25 86 / 1145 5/11 Ø O Ø O O O × Ø O × ×

    A.Portal(dp)

    题目描述

      有一个 (n) 个点 (m) 条边的带权图,你一开始在 (1) 号点,要按顺序完成 (k) 个任务,第 (i) 个任务是先去(a[i]) 再走到 (b[i])。当你走到一个点上的时候,可以在这个点创建一个传送门。当同时存在两个传送门的时候,你可以在传送门之间不耗代价地传送。如果已经存在了两个传送门,想再创建一个,就必须选择之前的一个传送门关掉(关掉这个操作不耗时间,并且是远程操作,不需要走过去)。问完成所有任务的最短总行走距离。

      数据范围:(1leq n,kleq 300,1leq mleq 40000)

    分析

      用动态规划求解。

      (f(i,u,x,y)) 表示已经完成了第 (i) 个任务,当前人在节点 (u),传送门在节点 (x)(y) 时,行走的最短距离。状态过多,显然会 ( ext{MLE})( ext{TLE})

      贪心地思考,一直创建两个传送门是没有必要的:若要从 (x) 传送到 (y),当前节点为 (u),那么必须要从 (u) 走到 (x) 再传送到 (y);不妨只在 (y) 创建一个传送门,走到 (x) 后再设置传送门;也就是说,我们可以随时在当前节点创建传送门。因此,只需要在状态中记录一个传送门的位置即可。(f(i,u,p)) 表示已经完成了第 (i) 个任务,当前人在节点 (u),传送门在节点 (p) 时,行走的最短距离。需要继续精简状态。

      不妨将 (k) 个任务看作一条路径:(1 o a_1 o b_1 ocdots o a_n o b_n)。一共有 (t=2k+1) 个节点,(c_i) 表示第 (i) 个节点,其中 (1leqslant ileqslant t)(f(i,p)) 表示当前人位于节点 (c_i),传送门位于节点 (p) 时,行走的最短距离。

      可以证明,只需要三种转移,即可覆盖所有状态:① 直接从 (c_{i-1})走到 (c_i),不更改传送门位置;② 枚举 (q),将传送门的位置更改到 (q),从 (c_{i-1}) 传送到 (p),再从 (p) 走到 (q),将传送门放在 (q),再从 (q) 走到 (c_i);③ 枚举 (q),将传送门的位置更改到 (q),从 (c_{i-1}) 走到 (q),将传送门放在 (q),再从 (q) 传送到 (p),从 (p) 走到 (c_i)

    代码

    /*******************************************************************
    Copyright: 11D_Beyonder All Rights Reserved
    Author: 11D_Beyonder
    Problem ID: 2020牛客暑期多校训练营(第五场) Problem A
    Date: 8/24/2020
    Description: Dynamic Programming
    *******************************************************************/
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const ll inf=0x3f3f3f3f3f3f3f3f;
    const int N=705;
    //====================
    //f[i][j]表示:
    // 当前在c[i],传送门在j时;
    // 行走的最小路程。
    ll f[N][N];
    ll dis[N][N];
    int c[N];
    int n,m;
    int t;
    int main(){
    	int i,j,k;
    	cin>>n>>m>>k;
    	memset(f,inf,sizeof(f));
    	memset(dis,inf,sizeof(dis));
    	for(i=1;i<=n;i++) dis[i][i]=0;
    	for(i=1;i<=m;i++){
    		int u,v;
    		ll w;
    		scanf("%d%d%lld",&u,&v,&w);
    		dis[u][v]=min(dis[u][v],w);
    		dis[v][u]=min(dis[v][u],w);
    	}
    	c[++t]=1;
    	for(i=1;i<=k;i++){
    		int a,b;
    		scanf("%d%d",&a,&b);
    		c[++t]=a;
    		c[++t]=b;
    	}
    	//Floyed算法求(x,y)之间的最短路
    	for(k=1;k<=n;k++){
    		for(i=1;i<=n;i++){
    			for(j=1;j<=n;j++){
    				dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
    			}
    		}
    	}
    	for(i=1;i<=n;i++){
    		//初始化
    		//当前在c[1]
    		//传送门设置在i
    		f[1][i]=dis[1][i];
    	}
    	int p,q;
    	for(i=2;i<=t;i++){
    		//当前在c[i-1],要走向c[i]
    		for(p=1;p<=n;p++){
    			//当前传送门在p
    			//不改变传送门位置,直接走到 c[i]
    			f[i][p]=min(f[i][p],f[i-1][p]+dis[c[i-1]][c[i]]);
    			for(q=1;q<=n;q++){
    				//从c[i-1]传送到p,路程0
    				//从p走到q,路程dis[p][q]
    				//从q走到a[i],路程dis[q][c[i]]
    				f[i][q]=min(f[i][q],f[i-1][p]+dis[p][q]+dis[q][c[i]]);
    				//从c[i-1]走到q,路程dis[c[i-1]][q]
    				//从q传送到p,路程为0
    				//从p走到c[i],路程dis[q][c[i]]
    				f[i][q]=min(f[i][q],f[i-1][p]+dis[c[i-1]][q]+dis[p][c[i]]);
    			}
    		}
    	}
    	ll ans=inf;
    	for(i=1;i<=n;i++){
    		ans=min(f[t][i],ans);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    B.Graph(异或最小生成树)

    题目描述

      给一棵 (n) 个节点的树,每条边都有一个权值,每次可以做一个操作:加入一条边或者删除一条边。最终使得所有边的权值和最小。

      在加入或删除边的时候要满足以下两个条件:

      (1.) 图始终保持联通。

      (2.) 每个环上的边的异或和为 (0)

      数据范围:(2leq nleq 10^5,0leq x,yleq n-1,0leq z<2^{30})

    分析

      可以发现,无论添加边的时间顺序,连接点 (a) 和点 (b) 的边的权值一定是固定的,值为点 (a) 到点 (b) 路径上的所有边权的异或值,所以题目可以简化成寻找完全图的最小生成树。

      设 (dist[x]) 为点 (0) 到点 (x) 上所有边权的异或值,则在完全图中,连接点 (a) 和点 (b) 的边的权值为 (dist[x]oplus dist[y]),这样就相当于每个点都有一个点权值 (dist[i]),用 (dfs) 处理即可。

      参考 CF888G 的做法,时间复杂度为 (O(nlog^2n))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200010;
    int trie[N*30][2],a[N],tot=0;
    struct Edge
    {
        int to;
        int dis;
        int Next;
    }edge[N<<1];
    int head[N],num_edge;
    void add_edge(int from,int to,int dis)
    {
        edge[++num_edge].to=to;
        edge[num_edge].dis=dis;
        edge[num_edge].Next=head[from];
        head[from]=num_edge;
    }
    void insert(int x)
    {
        int p=0;
        for(int i=30;i>=0;i--)
        {
            int ch=(x>>i)&1;
            if(!trie[p][ch])
                trie[p][ch]=++tot;
            p=trie[p][ch];
        }
    }
    int solve(int root1,int root2,int bit)
    {
        if(bit<0)
            return 0;
        int ans1=-1,ans2=-1;
        if(trie[root1][0]&&trie[root2][0])
            ans1=solve(trie[root1][0],trie[root2][0],bit-1);
        if(trie[root1][1]&&trie[root2][1])
            ans2=solve(trie[root1][1],trie[root2][1],bit-1);
        if(ans1>=0&&ans2>=0)
            return min(ans1,ans2);
        if(ans1>=0)
            return ans1;
        if(ans2>=0)
            return ans2;
        if(trie[root1][0]&&trie[root2][1])
            ans1=solve(trie[root1][0],trie[root2][1],bit-1)+(1<<bit);
        if(trie[root1][1]&&trie[root2][0])
            ans2=solve(trie[root1][1],trie[root2][0],bit-1)+(1<<bit);
        if(ans1>=0&&ans2>=0)
            return min(ans1,ans2);
        if(ans1>=0)
            return ans1;
        if(ans2>=0)
            return ans2;
    }
    long long ans=0;
    void dfs(int start,int bit)
    {
        if(bit<0)
            return ;
        if(trie[start][0]&&trie[start][1])
            ans=ans+1ll*solve(trie[start][0],trie[start][1],bit-1)+(1<<bit);
        if(trie[start][0])
            dfs(trie[start][0],bit-1);
        if(trie[start][1])
            dfs(trie[start][1],bit-1);
    }
    int dist[N];
    void init(int x,int fa)
    {
        for(int i=head[x];i;i=edge[i].Next)
        {
            int y=edge[i].to,z=edge[i].dis;
            if(y==fa)
                continue;
            dist[y]=dist[x]^z;
            init(y,x);
        }
    }
    int main()
    {
        int n;
        cin>>n;
        for(int i=1;i<=n-1;i++)
        {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            add_edge(x,y,z);
            add_edge(y,x,z);
        }
        init(0,0);
        for(int i=0;i<=n-1;i++)
            insert(dist[i]);
        dfs(0,30);
        cout<<ans<<endl;
        return 0;
    }
    

    C.Easy(生成函数)

    题目描述

      已知序列 (a,b) 满足 (displaystylesum_{i=1}^{k}a_i=n,displaystylesum_{i=1}^{k}b_i=m)(a_i,b_i) 均为 正整数),对于所有满足条件的 (a,b) 序列,求 (displaystyleprod_{i=1}^{k}min(a_i,b_i)) 的和。

      数据范围:(1leq Tleq 100,1leq n,mleq 10^6,1leq kleq min(n,m))

    分析

      假设 (N<M),构造满足 $displaystylesum_{i=1}{k}a_i=n,displaystylesum_{i=1}{k}b_i=m $ 的生成函数:

    [F(x,y)=(x+x^2+cdots+x^n)^k(y+y^2+cdots+y^m)^{k} ]

      显然多项式展开后,(x^ny^m) 的系数即为不同序列 $a,b $ 的方案数。

      由于每一组 (a_i,b_i) 对答案的贡献为 (min(a_i,b_i)),因此构造本题答案的生成函数需在含 (x,y) 的项之前乘上 (min(a_i,b_i)),构造生成函数 (S=displaystylesum_{i=1}^{infty}sum_{j=1}^{infty}min(i,j)x^iy^j),答案即为 (S^k) 的 $xnym $ 的系数。

      求出 (S) 的封闭形式:

    [egin{aligned}S&=xy+xy^2+xy^3+xy^4+xy^5+cdots\ &+x^2y+2x^2y^2+2x^2y^3+2x^2y^4+2x^2y^5+cdots\ &+x^3y+2x^3y^2+3x^3y^3+3x^3y^4+3x^3y^5+cdots\ xS&=x^2y+x^2y^2+x^2y^3+x^2y^4+x^2y^5+cdots\ &+x^3y+2x^3y^2+2x^3y^3+2x^3y^4+2x^3y^5+cdots\ &+x^4y+2x^4y^2+3x^4y^3+3x^4y^4+3x^4y^5+cdots\ (1-x)S&=xy+xy^2+xy^3+xy^4+xy^5+cdots\ &+ x^2y^2+x^2y^3+x^2y^4+x^2y^5+cdots\ &+ x^3y^3+x^3y^4+x^3y^5+cdots\ S&=frac{xy(1+y(x+1)+y^2(x^2+x+1)+cdots)}{1-x}\ yS&=frac{xy(y+y^2(x+1)+y^3(x^2+x+1)+cdots)}{1-x}\ (1-y)S&=frac{xy(1+yx+y^2x^2+dots)}{1-x}\ S&=frac{xy(1+yx+y^2x^2+cdots)}{(1-x)(1-y)}\ xyS&=frac{xy(yx+y^2x^2+cdots)}{(1-x)(1-y)}\ S&=frac{xy}{(1-x)(1-y)(1-xy)}=xyG(x)G(y)G(xy) end{aligned} ]

      因此 (S^k=x^ky^kG^k(x)G^k(y)G^k(xy)),由于 (G^k(x)=displaystylesum_{i=0}^{infty}dbinom{k+i-1}{i}x^i),答案为 $xnym $ 的系数,即:

    [ans=sum_{i=0}^{min(n,m)-k}dbinom{k+i-1}{i}·dbinom{k+(n-k-i)-1}{k-1}·dbinom{k+(m-k-i)-1}{k-1}\ =sum_{i=0}^{min(n,m)-k}dbinom{k+i-1}{i}·dbinom{n-i-1}{k-1}·dbinom{m-i-1}{k-1} ]

      时间复杂度 (O(Tmin(n,m)))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=998244353,N=2e6+10;
    long long fac[N+10],inv[N+10];
    long long quick_pow(long long a,long long b)
    {
        long long ans=1;
        while(b)
        {
            if(b&1)
                ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    long long C(long long n,long long m)
    {
        return fac[n]*inv[m]%mod*inv[n-m]%mod;
    }
    int main()
    {
        fac[0]=1;
        for(int i=1;i<=N;i++)
            fac[i]=fac[i-1]*i%mod;
        inv[N]=quick_pow(fac[N],mod-2);
        for(int i=N;i>=1;i--)
            inv[i-1]=inv[i]*i%mod;
        int T;
        cin>>T;
        while(T--)
        {
            long long n,m,k;
            cin>>n>>m>>k;
            int minn=min(n,m);
            long long ans=0;
            for(int i=0;i<=minn-k;i++)
            {
                ans=(ans+C(k+i-1,i)*C(n-i-1,k-1)%mod*C(m-i-1,k-1)%mod)%mod;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

    D.Drop Voicing(断环成链+LIS)

    题目描述

      给定一个长为 (n(2leq nleq 500)) 的排列,有两种操作:

      (1.) 将倒数第二个数放到开头。

      (2.) 将第一个数放到最后。

      连续的操作 (1)(包括 (1) 次)称为一段。现在要将排列变成 (1) ~ (n),要使得段数尽可能少,求最小值。

    分析

      对于操作 ( ext{Drop-2}),可以将 (p_1) ~ (p_{n-1}) 看作一个环,环的长度为 (n-1),即进行 (n-1) 次操作 ( ext{Drop-2}),排列还原;对于操作 ( ext{Invert}),可以将 (p_1) ~ (p_n) 看作一个环,环的长度为 (n),即进行 (n) 次操作 ( ext{Invert}),排列还原。形成的两个环如图所示,$color{red}surd $ 代表当前排列 (p) 的第一个数,(color{red} imes) 代表位于大环(长度为 (n) 的环)上,而在小环(长度为 (n-1) 的环)外的数。

    代码

    /******************************************************************
    Copyright: 11D_Beyonder All Rights Reserved
    Author: 11D_Beyonder
    Problem ID: 2020牛客暑期多校训练营(第五场) Problem D
    Date: 8/20/2020
    Description: Circle, LIS
    *******************************************************************/
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=504;
    int n;
    int p[N];
    int a[N];
    int dp[N];
    int main(){
        cin>>n;
        int i,j;
        for(i=1;i<=n;i++){
            scanf("%d",p+i);
        }
        int ans=0x3f3f3f3f;
        //枚举环的起点
        for(i=1;i<=n;i++){
            for(j=1;j<=n;j++){
                //环确定了起点为i
                //于是可以环拉成链
                a[j]=p[i+j-1-n*(i+j-1>n)];
            }
            //求LIS
            int len=1;
            dp[1]=a[1];
            for(j=2;j<=n;j++){
                if(a[j]>dp[len]){
                    dp[++len]=a[j];
                }else{
                    *lower_bound(dp+1,dp+1+len,a[j])=a[j];
                }
            }
            ans=min(n-len,ans);//最小调整次数
        }
        cout<<ans<<endl;
        return 0;
    }
    

    E.Bogo Sort(置换+LCM)

    题目描述

      给出一个置换 (p),问 (1) ~ (n)(n) 个数有多少种排列,能经过若干次 (p) 的置换变为有序序列。答案对(10^n) 取模((1leq nleq 10^5))。

    分析

      在 ( ext{Tonnnny Sort})( ext{shuffle function}) 中,有操作 (a_i=b_{p_i}),实际上是用置换 (p) 将原序列 (a) 映射到当前的序列 (a)。如 (p=[3,5,4,1,2]),那么就有:(a_1=b_3)(a_3=b_4)(a_4=b_1),形成了 (3 o 1 o4 o3 ocdots) 的闭环,即 (a_1,a_3,a_4) 三者的值进行了交换;同理,有 (5 o2 o5 ocdots) 这样的闭环。

      问题转化为:给定置换 (p),求多少种排列可以通过置换 (p) 完成排序。不妨考虑将排序后的序列 (a) 用置换 (p) 打乱会产生多少种不同序列。设排序后的序列为 (a=[a_1,a_2,cdots,a_n])(p)(m) 个环,且各个环的长度为 (c_1,c_2,cdots,c_m),显然,利用置换 (p) 进行 (mathrm{lcm}(c_1,c_2,cdots,c_m))( ext{shuffle function})(a) 回到最初排完序的状态,而每次操作后得到的序列都是不同的。因此,只要找出置换 (p) 所有的环,所有环长的最小公倍数即为答案。

      值得注意的是,数据范围较大,可以使用 ( ext{Java})( ext{BigInteger}) 类。并且,所有环的长度总和为 (n),所以所有环的长度的最小公倍数不可能超过 (10^n),因此最后不必将答案对 (10^n) 取模。

    代码

    /******************************************************************
    Copyright: 11D_Beyonder All Rights Reserved
    Author: 11D_Beyonder
    Problem ID: 2020牛客暑期多校训练营(第五场) Problem E
    Date: 8/20/2020
    Description: Group Theory, BigInteger
    *******************************************************************/
    import java.math.BigInteger;
    import java.util.Scanner;
    public class Main{
    	public static void main(String[] args){
    		final int N=100005;
    		Scanner in=new Scanner(System.in);
    		int n=in.nextInt();
    		BigInteger[] cycle=new BigInteger[N];//环长度
    		boolean[] vis=new boolean[N];
    		int[] p=new int[N];
    		int m=0;
    		int i;
    		for(i=1;i<=n;i++){
    			p[i]=in.nextInt();
    		}
    		for(i=1;i<=n;i++){
    			int len=0;
    			int pos=i;
    			while(!vis[pos]){
    				//遍历环
    				//记录访问
    				len++;
    				vis[pos]=true;
    				pos=p[pos];
    			}
    			if(len>0) cycle[++m]=BigInteger.valueOf(len);
    		}
    		BigInteger ans=cycle[1];
    		//求环长度的最大公约数
    		for(i=2;i<=m;i++){
    			ans=cycle[i].multiply(ans).divide(cycle[i].gcd(ans));
    		}
    		System.out.println(ans);
    	}
    }
    

    F.DPS(模拟)

    题目描述

      给出 (n(1leq nleq 100)) 名玩家的伤害值 (d_i(0leq d_ileq 43962200)),绘制伤害的直方图(最大值要在图中作出标记),长度为 (s_i=lceil50frac{d_i}{max{d_i}} ceil)

    分析

      按照题意模拟即可,注意计算 (s_i) 时会爆 int

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int a[1010];
    int main()
    {
        int n,maxn=-1;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            maxn=max(maxn,a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            int temp=ceil(50.0*a[i]/maxn);
            printf("+");
            for(int j=1;j<=temp;j++)
                printf("-");
            printf("+
    ");
            if(a[i]!=maxn)
            {
                printf("|");
                for(int j=1;j<=temp;j++)
                    printf(" ");
                printf("|");
                printf("%d",a[i]);
                puts("");
            } else{
                printf("|");
                for(int j=1;j<=temp-1;j++)
                    printf(" ");
                printf("*|");
                printf("%d",a[i]);
                puts("");
            }
            printf("+");
            for(int j=1;j<=temp;j++)
                printf("-");
            printf("+
    ");
        }
        return 0;
    }
    

    H.Interval(主席树)

      给定一个序列 (A),长度为 (N),定义函数(F(l,r)=A_l & A_{l+1}&…&A_r),集合(S(l,r)={F(a,b)|min(l,r)leqslant aleqslant bleqslant max(l,r)}),即对 ([l,r]) 的所有子区间求 (F) 的值并去重,有 (Q) 次询问,每次询问给出 (l,r),求 (S(l,r)) 的元素个数。

      该题目强制在线,(L=(L'oplus lastans) \% N+1)(R=(R'oplus lastans)\%N+1)

    分析

      考虑对于 (1)(N) 的位置建立普通线段树,维护每个位置出现的不同数字个数。对于序列前 (x) 个元素,当查询的区间右界 (R=x) 的时候,若 (F(y,x)=k),那么对于任意 (Lleqslant y) 都满足 (kin S(L,x)),因此对于每一个数字 (F) 的值,只要维护它最靠右的出现位置即可,而且对于每一个 (F),只能出现在一个位置,不可以重复计数。对于数字的去重,可以通过 unordered_map 来实现。

      根据上述分析,查询 (S(L,R)) 的时候,需要在 ([1,R]) 上建立的线段树中查询 ([L,R]) 的区间,因此需要对每一个位置为区间右界构造线段树,为了更高效,采用主席树来实现。显然,当 (R=1) 的时候,整个线段树有且只有一个位置有 (1) 的权值,而对于任意 (R'=R+1),新的区间的所有 (F) 值必然包含原来的区间,因此对 ([1,R]) 建树的时候所求得的 (S(1,R)) 需要进行记录,而对于 (S(1,R')),除了包含(S(1,R)) 的所有元素以外,还额外包含了(A_{R'}) 以及 (S(1,R)) 中的所有元素与 (A_{R'}) 按位与的结果,将这些新的元素加入 (S(1,R)) 去重后即可得到 (S(1,R'))。在去重的时候需要始终维护所有数字只保留出现位置最靠后的一个。在主席树创建一个新树的时候,添加的新元素与已经存在的元素发生重复,需要在新树上进行修改,在该元素之前出现的位置进行 update(-1) 的操作,在该元素更新后的位置进行update(1) 的操作,也就是修改其最后出现的位置。

      建树完成之后,每次查询只要在 (root[R]) 的树上对区间 ([L,R]) 进行查询即可。

    代码

    #include<bits/stdc++.h>
    #include<unordered_map>
    #define mid (l+r)>>1
    using namespace std;
    const int maxn = 30000005;
    int ls[maxn], rs[maxn], val[maxn], root[100005];//左右儿子,权值,主席树的不同根
    unordered_map<int, int>mp, la, tmp;//当前树去重得到的元素集合,上一棵树的元素集合,临时辅助集合
    int tot;//中结点个数
    int newnode(int rt, int v)//动态开点
    {
    	val[++tot] = val[rt] + v;//直接在开点的时候修改权值
    	ls[tot] = ls[rt];
    	rs[tot] = rs[rt];
    	return tot;
    }
    void update(int& now, int la, int pos, int v, int l, int r)//更新,la代表上一棵树的同位置根节点
    {
    	if (l > pos || r < pos)
    		return;
    	now = newnode(la, v);//动态开点
    	if (l == r)return;
    	int m = mid;
    	update(ls[now], ls[la], pos, v, l, m);
    	update(rs[now], rs[la], pos, v, m + 1, r);
    }
    int query(int now, int L, int R, int l, int r)//普通二叉树区间查询
    {
    	if (l > R || r < L)return 0;
    	if (l >= L && r <= R)return val[now];
    	int m = mid;
    	return query(ls[now], L, R, l, m) + query(rs[now], L, R, m + 1, r);
    }
    int main()
    {
    	int n;
    	cin >> n;
    	for (int i = 1; i <= n; i++)
    	{
    		int x;
    		scanf("%d", &x);
    		root[i] = root[i - 1];//复制新根
    		mp[x] = i;//插入新的数字x,位置为i
    		tmp.clear();
    		for (auto it : mp)
    		{
    			tmp[it.first & x] = max(tmp[it.first & x], it.second);//计算出所有新增F的值,存在tmp中
    		}
    		for (auto it : tmp)
    		{
    			if (la[it.first] == it.second)continue;//如果某元素已经存在过了,而且位置没有发生改变,就不进行更新
    			update(root[i], root[i], la[it.first], -1, 1, n);//删去原来的位置
    			la[it.first] = it.second;//在存放历史树信息的集合中更新数据
    			update(root[i], root[i], la[it.first], 1, 1, n);//插入在新的位置
    		}
    		mp.swap(tmp);//更新mp集合
    	}
    	int q;
    	int lastans = 0;
    	cin >> q;
    	while (q--)
    	{
    		int l, r;
    		scanf("%d%d", &l, &r);
    		l = (l ^ lastans) % n + 1;//强制在线
    		r = (r ^ lastans) % n + 1;
    		if (l > r)swap(l, r);
    		lastans = query(root[r], l, r, 1, n);//查询
    		printf("%d
    ", lastans);
    	}
    	return 0;
    }
    

    I.Hard Math Problem(数学)

    题目描述

      有一个 (n imes m)的矩阵,以及三个角色:总部、金矿工和收藏家,在矩阵的每个点放置一名角色,要求总部 (H) 的旁边至少有一个金矿工 (G) 和收藏家 (E)。问如何排布能使这种总部数量最多。

    分析

      用 img 代表总部,img 代表黄金矿工,img 代表收藏家,如图的结构能够使总部的数量尽量多。

      对于一个无穷网络,一个单元已经用虚线框出。一个总部分到 (frac{1}{2}) 个黄金矿工和 (frac{1}{2}) 个收藏家。一个单元所占格子数量为 (frac{3}{2}),一个总部占整个单元的 (frac{2}{3})

  • 相关阅读:
    遗传算法
    UVa 11584 Partitioning by Palindromes
    UVa1625 Color Length
    UVa10003 Cutting Sticks
    UVa1347 Tour
    UVa116 (单向TSP,多决策问题)
    uVa 12563 Jin Ge Jin Qu
    模糊综合评判
    Python进阶(5)_进程与线程之协程、I/O模型
    Python进阶(4)_进程与线程 (python并发编程之多进程)
  • 原文地址:https://www.cnblogs.com/ResuscitatedHope/p/13811618.html
Copyright © 2011-2022 走看看