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

    Name Date Rank Solved A B C D E F G H I J K L
    2020 Multi-University,Nowcoder Day 2 2020.7.18 78/1178 8/12 O O O O O O O Ø × × × O

    A.Clam and Fish(贪心)

    题目描述

      一共 (n(1leq nleq 2 imes 10^6)) 天,每天有下述四种状态中的一种:

    • 无鱼无饵
    • 无鱼有饵
    • 有鱼无饵
    • 有鱼有饵

      有鱼可以拿鱼,有饵可以拿饵,无鱼可以用一个饵拿鱼,或什么都不做,四种操作只能选一个,求最多能拿多少鱼。

    分析

      首先明确,要是当前的状态有鱼,那么直接抓鱼。若当前为状态 (2),那么用鱼饵钓鱼显然不如直接抓鱼实惠;若当前状态为 (3),虽然可以获得鱼饵,但是获得鱼饵也是为了在将来花费这个鱼饵钓到鱼,问题是将来可能不会再有钓鱼的机会(鱼饵多余,已经是最后一天……),不如放弃这个鱼饵,直接抓鱼。
      若当前状态没有鱼,那么鱼饵要尽量在没有蛤蜊的时候用掉,有蛤蜊的状态用来获取鱼饵。若当前状态为 (0),什么都没有;那么有鱼饵的话直接用来抓鱼,否则就什么都不做。若当前状态为 (1),如果没有鱼饵,那就不必思考,直接获取鱼饵;如果有鱼饵,再来考虑要不要钓鱼;因为鱼饵是为将来状态为 (0) 的时候准备的,如果钓鱼后,拥有的鱼饵足够将来状态为 (0) 的那几天使用,就可以放心地钓鱼,否则就将蛤蜊做成鱼饵。

      值得一提的是,需要逆向预处理第 (i) 天后状态为 (0) 的天数,再正向进行贪心。

    代码

    /******************************************************************
    Copyright: 11D_Beyonder All Rights Reserved
    Author: 11D_Beyonder
    Problem ID: 2020牛客暑期多校训练营(第三场) Problem A
    Date: 8/14/2020
    Description: Greedy
    *******************************************************************/
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=2000005;
    int _;
    char days[N];
    int n;
    int leftover[N];
    int bait,fish;
    inline void init();
    int main(){
        for(cin>>_;_;_--){
            scanf("%d%s",&n,days+1);
            init();
            int i;
            for(i=n;i>=1;i--){
                leftover[i]+=days[i]=='0';
                leftover[i]+=leftover[i+1];
            }
            for(i=1;i<=n;i++){
                if(days[i]=='0'){
                    //没鱼 没蛤蜊
                    if(bait){
                        //有鱼饵 可以抓鱼
                        bait--;
                        fish++;
                    }
                }else if(days[i]=='1'){
                    //没鱼 有蛤蜊
                    if(!bait){
                        //没鱼饵 只能将蛤蜊做成鱼饵
                        bait++;
                    }else{
                        //有鱼饵 考虑要不要抓鱼
                        if(leftover[i]<bait){
                            //当前鱼饵足够在后面没鱼没蛤蜊的情况下抓鱼
                            //现在可以抓鱼
                            fish++;
                            bait--;
                        }else{
                            //做成鱼饵 在将来没鱼没蛤蜊的情况下抓鱼
                            bait++;
                        }
                    }
                }else{
                    //有鱼直接抓 不能错失机会
                    fish++;
                }
            }
            printf("%d
    ",fish);
        }
        return 0;
    }
    inline void init(){
        bait=fish=0;
        fill(leftover,leftover+n+2,0);
    }
    

    B.Classical String Problem(模拟)

    题目描述

      给一个字符串 (S),下标从 (1) 开始,(q) 次询问,每一组有一个字符 (op),一个数字 (x),当 (op=A) 时,输出当前字符串第 (x) 个字符;当 (op=M) 时,如果 (x>0),把字符串最左边的 (x) 个字符放到字符串右边,如果 (x<0),把字符串最右边 (|x|) 个字符放到字符串左边。

      数据范围:(2leq |S|leq 2 imes 10^6,1leq qleq 8 imes 10^5)

    分析

      小模拟,对于一个字符串,存在两种操作,一种是将字符串长度为 (x) 的前缀移动到后面,或者将后缀移动到前面,另一种是查询当前第 (x) 个字符是什么。对于前后缀的移动可以认为字符串是一个环形的,只要移动环形的入口位置即可,定义变量 (base) 为第(0)个字符,移动的时候让 (base) 加上 (x),查询的时候查询(base+x) 位置的字符。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
    	string str;
    	cin >> str;
    	int base = -1;
    	int len = str.size();
    	int n;
    	cin >> n;
    	while (n--)
    	{
    		char s[2];
    		int x;
    		scanf("%s%d", s, &x);
    		if (s[0] == 'A')
    		{
    			int tmp = base + x;
    			if (tmp < 0)
    			{
    				tmp += len * ((abs(tmp)) / len + 1);
    			}
    			tmp %= len;
    			putchar(str[tmp]);
    			putchar('
    ');
    		}
    		else
    		{
    			base += x;
    		}
    	}
    	return 0;
    }
    

    C.Operation Love(思维+模拟)

    题目描述

      给出一个手掌的形状((20) 个点),判断图形是左手还是右手(可能旋转 (45^{circ}),但大小不会变化)。

    分析

      首先观察得仅存在一个点((O)):大拇指根部的点,与其相连的两条边长度分别是 (6)(9),那么遍历所有的点,找到其与相邻两点之间的距离,最终定位到点 (O),并将整个图形进行平移,让点 (O) 与原点重合,与(O) 相连的两个边形成一个直角,此时只需要判断与 (O) 相邻的两个点在坐标轴上的关系即可判断是左右手。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef double db;
    struct node
    {
    	db x, y;
    };
    node input[20];
    node num[20];
    int qq, hj;
    db dis(node& a, node& b)
    {
    	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    }
    void trans()
    {
    	int base = 0;
    	for (int i = 0; i < 20; i++)
    	{
    		int l = (i - 1 + 40) % 20;
    		int r = (i + 1) % 20;
    		db ll = dis(input[i], input[l]);
    		db rr = dis(input[i], input[r]);
    		if ((fabs(ll - 9.0) < 1e-2 && fabs(rr - 6.0) < 1e-2) || (fabs(ll - 6.0) < 1e-2 && fabs(rr - 9.0) < 1e-2))
    		{
    			base = i;
    			break;
    		}
    	}
    	for (int i = 0; i < 20; i++)
    	{
    		num[i] = input[(i + base) % 20];
    	}
    	for (int i = 19; i >= 0; i--)
    	{
    		num[i].x -= num[0].x;
    		num[i].y -= num[0].y;
    	}
    	if (fabs(dis(num[0], num[1]) - 9.0) < 1e-2)
    	{
    		hj = 1;
    		qq = 19;
    	}
    	else
    	{
    		hj = 19;
    		qq = 1;
    	}
    }
    int main()
    {
    	int t;
    	cin >> t;
    	while (t--)
    	{
    		for (int i = 0; i < 20; i++)
    		{
    			scanf("%lf%lf", &input[i].x, &input[i].y);
    		}
    		trans();
    		node chang = num[hj];
    		node duan = num[qq];
    		bool wa = false;
    		if (duan.x >= 0.0 && duan.y > 0.0)
    		{
    			if (chang.x > 0.0 && chang.y <= 0.0)wa = true;
    		}
    		else if (duan.x > 0.0 && duan.y <= 0.0)
    		{
    			if (chang.x <= 0.0 && chang.y < 0.0)wa = true;
    		}
    		else if (duan.x <= 0.0 && duan.y < 0.0)
    		{
    			if (chang.x < 0.0 && chang.y >= 0.0)wa = true;
    		}
    		else
    		{
    			if (chang.x >= 0.0 && chang.y > 0.0)wa = true;
    		}
    		if (wa)puts("right");
    		else puts("left");
    	}
    	return 0;
    }
    

    D.Points Construction Problem(构造)

    题目描述

      在二维平面内,每个格点(整数点)有一个白点,可以将其中一些点涂黑。问能否将 (n(1leq nleq 50)) 个白点涂黑,使得有 (m(1leq mleq 200)) 对相邻的白点和黑点(相邻指曼哈顿距离为 (1))。

    分析

      在平面上用小正方形构造一个可以分散的,面积为 (n),周长和为 (m) 的不规则图形。首先考虑 (n)(m) 的关系,显然,当 (n) 个小正方形互相不接触的时候是周长和最大的情况,此时 (m=4 imes n),当 (n) 个小正方形尽可能堆积成一个大正方形的时候,周长是最小的,此时 (m=2 imes(lceilsqrt{n} ceil+lceilfrac{n}{lceilsqrt{n} ceil} ceil)),另外,考虑对于每一个不规则图形的一条边,总存在另一条边与其对应,周长应该总是一个偶数,当 (m) 是奇数的时候同样无法构造答案。

      具体构造方法,可以认为一开始所有的小正方形都是排列在一条斜线上的,此时总周长是 (4 imes n),此时可以将后面的小正方形一个个取出来,放在与前面正方形相接触的位置,如果放置的位置与一个正方形相接触,总周长 (-2),如果和两个接触,总周长 (-4)。再考虑要保证能够构造出周长最小的情况,因此需要一层一层来膜最开始的一个正方形,具体构造顺序可以认为如下:

    1 2 5 10 17
    3 4 6 11 18
    7 8 9 12 19
    13 14 15 16 20
    21 22 23 24 25

      一直按这样的顺序来构造,直到最后总周长等于 (m) 的时候停止,此时剩余的所有方块在一个较远的位置摆放成一条斜线。需要特别注意,如果某一时刻要构造的位置会让周长减 (4),但是此时周长和 (m) 的差值只有 (2),可以特别地在第一个方块的上面放一个方块,然后结束构造,即可规避这种特殊情况。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int mp[50][50];
    int main()
    {
    	int t;
    	cin >> t;
    	while (t--)
    	{
    		memset(mp, 0, sizeof(mp));
    		int n, m;
    		scanf("%d%d", &n, &m);
    		if (m & 1 || m > 4 * n)
    		{
    			puts("No");
    			continue;
    		}
    		int tmp = 4 * n - m;
    		mp[1][1] = 1;
    		int step = 2;
    		while (tmp && step <= 10)
    		{
    			int tt = 2 * step - 1;
    			tt *= 4;
    			tt -= 4;
    			if (tmp >= tt)
    			{
    				for (int i = 1; i <= step; i++)
    				{
    					mp[step][i] = mp[i][step] = 1;
    				}
    				step++;
    				tmp -= tt;
    				continue;
    			}
    			if (tmp)
    			{
    				mp[step][1] = 1;
    				tmp -= 2;
    			}
    			int cnt = 2;
    			while (tmp >= 4 && cnt < step)
    			{
    				mp[step][cnt] = 1;
    				tmp -= 4;
    				cnt++;
    			}
    			if (tmp)
    			{
    				mp[1][step] = 1;
    				tmp -= 2;
    			}
    			cnt = 2;
    			while (tmp >= 4 && cnt <= step)
    			{
    				mp[cnt][step] = 1;
    				tmp -= 4;
    				cnt++;
    			}
    			if (tmp)
    			{
    				mp[0][1] = 1;
    				tmp -= 2;
    			}
    			step++;
    		}
    		int sum = 0;
    		for (int i = 0; i <= 10; i++)
    		{
    			for (int j = 0; j <= 10; j++)
    			{
    				if (mp[i][j])sum++;
    			}
    		}
    		if (sum > n)puts("No");
    		else
    		{
    			puts("Yes");
    			n -= sum;
    			for (int i = 0; i <= 10; i++)
    			{
    				for (int j = 0; j <= 10; j++)
    				{
    					/*if (mp[i][j])
    						printf("■");
    					else printf("  ");*/
    					if (mp[i][j])
    						printf("%d %d
    ", i, j);
    				}
    				/*putchar('
    ');*/
    			}
    			for (int i = 0; i < n; i++)
    			{
    				printf("%d %d
    ", i + 1000, i + 1000);
    			}
    		}
    	}
    	return 0;
    }
    

    E.Two Matchings(dp)

    题目描述

      (p=[p_1,p_2,cdots,p_n]) 是一个长为 (n) 的全排列。

      定义一个全排列是一个 匹配,当且仅当 (forall i,p_i eq i 且 p_{p_i}=i)

      对于数组 (a=[a_1,a_2,cdots,a_n](0leq a_ileq 10^9,ngeq 4))(n) 是偶数,定义全排列的价值为 (frac{displaystylesum_{i=1}^{n}Big|a_i-a_{p_i}Big|}{2})

      定义两个 匹配 (p,q)可组合 的,当且仅当 (forall i,p_i eq q_i)

      找到两个 可组合匹配,使得这两个 匹配 的总价值尽可能小,输出价值之和。

      数据范围:(n) 的总和不超过 (2 imes 10^5,0leq a_ileq 10^9)

    分析

      要想使总价值最小,需要找到价值最小的排列和价值最小的排列。

      价值最小的排列很容易找到,把 (a) 序列排序后两两配对计算价值即可。

      下文中均假设序列已经排好序。

      对于 (a) 序列有 (4) 个数字的情况,可以发现次小价值为 (a[4]-a[2]+a[3]-a[1]=a[4]+a[3]-a[2]-a[1])

      对于有 (a) 序列 (6) 个数字的情况,可以发现次小价值为 (a[3]-a[2]+a[5]-a[4]+a[6]-a[1])

      对于更长的序列,一定可以拆分成长为 (4) 和长为 (6) 的序列,转化成了子问题

      设 (dp[i]) 为前 (i) 个数的次大 价值

      状态转移方程为:

    [dp[i]=min(dp[i-4]+a[i]+a[i-1]-a[i-2]-a[i-3],dp[i-6]+a[i]+a[i-1]+a[i-3]-a[i-2]-a[i-4]-a[i-5]) ]

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200100;
    const long long INF=1ll<<60;
    long long a[N],dp[N];
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            long long ans=0,n;
            scanf("%lld",&n);
            for(int i=1;i<=n;i++)
                scanf("%lld",&a[i]);
            sort(a+1,a+1+n);
            for(int i=2;i<=n;i=i+2)
                ans=ans+a[i]-a[i-1];
            dp[2]=INF;
            dp[4]=a[4]+a[3]-a[2]-a[1];
            dp[6]=a[6]+a[5]+a[3]-a[4]-a[2]-a[1];
            for(int i=8;i<=n;i=i+2)
                dp[i]=min(dp[i-4]+a[i]+a[i-1]-a[i-2]-a[i-3],dp[i-6]+a[i]+a[i-1]+a[i-3]-a[i-2]-a[i-4]-a[i-5]);
            printf("%lld
    ",ans+dp[n]);
        }
        return 0;
    }
    

    F.Fraction Construction Problem(exgcd+构造)

    题目描述

      给出两个数字 (a,b(1leq a,bleq 2 imes 10^6)),求任意一组满足下列条件的 (c,d,e,f)

    • (frac{c}{d}-frac{e}{f}=frac{a}{b})

    • (d<b,f<b)

    • (1leq c,eleq 4 imes 10^{12})

      若无解,输出 (-1 -1 -1 -1)(1leq Tleq 10^5)

    分析

      若 (gcd(a,b) eq 1) 时,令 (c=2a,d=b,e=a,f=b)

      若 (b=1),无解。

      若 (gcd(a,b)=1)

    [frac{c}{d}-frac{e}{f}=frac{cf-de}{df}=frac{a}{b} ]

      令 (df=kb)(k) 为正整数)。

      若 (b) 是质数,无解,因为 (b) 的因子只有 (1)(b)(d)(f) 只能取 (b) 的倍数,与 (d<b,f<b) 矛盾。

      若 (b) 是某个质数的幂次方(即 (p^k)),无解。设 (b=p^k,d=p^a),则 (f=p^{k-a}),由扩展欧几里得算法可知 (cf-de=a) 的充要条件是 (gcd(d,f)|a),所以 (gcd(d,f)=p^{min(a,k-a)})。但是由于 (gcd(a,b)=1),所以 (p mid a),即 (gcd(d,f)|a),与前面所述矛盾。

      所以 (b) 至少有两个质因子才有解,假设 (b=p_1^{k_1}p_2^{k_2}),则 (d=p_1^{k_1-1}p_2^{k_2},f=p_1^{k_1}),解方程 (p_1c+p_2^{k_2}e=1),解出 (c,e) 后,都乘上 (a),则:

    [frac{c}{d}-frac{e}{f}=frac{cf-ed}{df}=frac{a(c'p_1^{k_1}-e'p_1^{k_1-1}p_2^{k_2})}{p_1^{k_1-1}p_2^{k_2}p_1^{k_1}}=frac{ap_1^{k-1}}{p_1^{k_1-1}p_2^{k_2}p_1^{k_1}}=frac{a}{b} ]

    代码

    #include<bits/stdc++.h>
    using namespace std;
    long long T,a,b;
    long long exgcd(long long a,long long b,long long &x,long long &y)
    {
        if(!b)
        {
            x=1;
            y=0;
            return a;
        }
        long long gcd=exgcd(b,a%b,x,y);
        long long temp=x;x=y;y=temp-(a/b)*y;
        return gcd;
    }
    const int N=2e6+10;
    long long fac[N+10];
    bool vis[N+10];
    void init()
    {
        for(int i=2;i<=N;i++)
        {
            if(!vis[i])
            {
                for(int j=i;j<=N;j=j+i)
                {
                    if(!vis[j])
                        fac[j]=i;
                    vis[j]=1;
                }
            }
        }
    }
    int main()
    {
        init();
        long long T;
        cin>>T;
        while(T--)
        {
            long long a,b;
            scanf("%lld %lld",&a,&b);
            long long gcd=__gcd(a,b);
            a=a/gcd;b=b/gcd;
            if(gcd>1)
            {
                printf("%lld %lld %lld %lld
    ",a*2,b,a,b);
                continue;
            }
            if(b==1)
            {
                printf("-1 -1 -1 -1
    ");
                continue;
            }
            gcd=b;
            while(gcd%fac[b]==0)
                gcd=gcd/fac[b];
            if(gcd==1)
            {
                printf("-1 -1 -1 -1
    ");
                continue;
            }
            long long c;
            long long d=b/fac[b];
            long long e;
            long long f=b/gcd;
            exgcd(fac[b],gcd,c,e);
            c=c*a;
            e=e*a;
            e=-e;
            if(c<0)
            {
                swap(c,e);
                swap(d,f);
                c=-c;e=-e;
            }
            printf("%lld %lld %lld %lld
    ",c,d,e,f);
        }
        return 0;
    }
    
    

    G.Operating on a Graph(并查集+启发式合并)

    题目描述

      给一个 (n) 个点,(m) 条边的无向图。点的编号从 (0)(n-1)。一开始,点 (i) 属于团 (i)。定义团 (A) 与团 (B) 相连,当且仅当属于团 (A) 的点和属于团 (B) 的点之间至少有一条边相连。给定 (q) 次操作,每次操作给出 (o_i)。如果没有边属于团 (o_i),无事发生;否则所有与 (o_i) 相连的点都属于团 (o_i)。最后输出每个点属于哪个团。

      数据范围:(2leq nleq 8 imes 10^5,1leq mleq 8 imes 10^5,1leq qleq 8 imes 10^5)

    分析

      可以发现,每个点被询问后,该点与其相邻点永远处于相同集合。

      用并查集维护每个点属于哪个集合,再用 (vector) 存储该集合的外部连接点是哪几个。更新外部连接点的时候按照集合大小来合并,不然超内存。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int fa[800010];
    int get(int x)
    {
        if(fa[x]==x)
            return x;
        return fa[x]=get(fa[x]);
    }
    vector<int> G[800010];
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            int n,m;
            scanf("%d %d",&n,&m);
            for(int i=0;i<=n;i++)
            {
                fa[i]=i;
                G[i].clear();
            }
            for(int i=1;i<=m;i++)
            {
                int u,v;
                scanf("%d %d",&u,&v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
            int q;
            scanf("%d",&q);
            while(q--)
            {
                int o;
                scanf("%d",&o);
                if(fa[o]!=o)
                    continue;
                vector<int> new_G=G[o];
                G[o].clear();
                for(int i=0;i<new_G.size();i++)
                {
                    int v=get(new_G[i]);
                    if(v!=o)
                    {
                        fa[v]=o;
                        if(G[o].size()<G[v].size())//启发式合并
                            swap(G[o],G[v]);
                        for(int j=0;j<G[v].size();j++)
                            G[o].push_back(G[v][j]);
                    }
                }
            }
            for(int i=0;i<n;i++)
                printf("%d ",get(i));
            puts("");
        }
        return 0;
    }
    

    H.Sort the Strings Revision(笛卡尔树+分治)

    题目描述

      给定一个初始字符串 (s_0),长度为 (n),第(i)个字符是 (i\%10),给定一个序列 (p) 和一个字符数组 (d),存在字符串 (s_1) ~ (sn),对于每一个字符串 (s_i),可以由前一个字符串 (s_{i-1}) 通过将第 (p[i-1]) 个位置的字符更改为 (d[i]) 来构造。将 (s_0) ~ (s_n) 按照字典序排序。

    输入格式

      (T(1leq Tleq 10^3)) 组数据,首先输入 $n(1leq nleq 2 imes 10^6) $ 代表字符串长度以及需要构造的 (p)(d) 的长度,随后依次给出 (p_{seed},p_a,p_b,p_{mod}(0leq p_{seed},p_a,p_b<p_{mod}leq 10^9+7))(d_{seed},d_a,d_b,d_{mod}(0leq d_{seed},d_a,d_b< d_{mod}leq 10^9+7) 按照

    [egin{aligned} &seed=P_{seed}\ &for i=0 to n -1:\ & p[i]=i\ &for i=1 to n-1:\ & swap(p[seed\%(i+1)],p[i])\ & seed=(seed*P_a+P_b)\%P_{mod}\ end{aligned} ]

    的方式构造序列 (p),按照

    [egin{aligned} &seed=d_{seed}\ &for i=0 to n-1\ & d[i]=seed\%10\ & seed=(seed*d_a+d_b)\%d_{mod} end{aligned} ]

    的方式构造序列 (d),最终将 (n+1) 个字符串按照字典序从小到大将下标排序之后,输出(displaystyle{(sum_{i=0}^{n}(r_i*10000019^i))\%1000000007})

    输出格式

      考虑 (n+1) 个字符串,相邻两两之间最多只有一个字符不相同,而且每个字符只会被修改一次,修改之后可能会使字符串字典序变大,变小,也有可能完全不变。首先不考虑不变的情况,显然,对于第 (0) 个字符,当它发生改变的时候,可能会导致后面所有的字符串字典序变大或者变小,但是不论怎么变化,都会以当前位置为分界,将 (n+1) 个字符分成大小两段,而且不论两段内部如何排序,总是满足某一段的所有字符串大于另一段的所有字符串。在拆分后的任意一段内,可以再次找到最靠前的一个被修改的字符,继续将字符串分割成两部分,如此递归下去,便能得到所有字符串的大小关系。再考虑那些不对字符串字典序产生影响的改变,可以对这些改变忽视,只对有效的修改进行拆分,最终会得到数个小片段,一些小片段可能包含不止一个字符串,这些片段产生的原因就是那些没有发生改变的修改,对于这些字符串,根据题目要求,按照下标升序进行排列即可。
      对于序列 (p) 建立笛卡尔树,便能在 (O(n)) 的时间内处理出每一次分治的位置,为了使那些不产生影响的修改被忽视,可以将对应位置的 (p) 中元素赋值为一个极大值,这样在笛卡尔树建立的过程中会将他们下沉到整个树的最低端,也就不会对上面的分治产生影响。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod = 1000000007;
    const int maxn = 2000005;
    const int inf = 0x7fffffff;
    int st[maxn], l[maxn], r[maxn];
    int p[maxn], d[maxn];
    int rk[maxn];
    void Cartesian(int n)//建笛卡尔树
    {
    	int top = 0;
    	for (int i = 0; i < n; i++)
    	{
    		//l[i] = r[i] = -1;
    		while (top && p[st[top]] > p[i])
    		{
    			l[i] = st[top--];
    		}
    		if (top)
    		{
    			r[st[top]] = i;
    		}
    		st[++top] = i;
    	}
    }
    void dfs(int n, int L, int R, int rak)//分治
    {
    	if (p[n] == inf || L >= R)//当前片段仅有一个元素或者当前片段的修改无效,可以按下表顺序进行赋值
    	{
    		for (int i = L; i <= R; i++)rk[i] = rak + i - L;
    		return;
    	}
    	dfs(l[n], L, n, rak + (p[n] % 10 > d[n]) * (R - n));
    	dfs(r[n], n + 1, R, rak + ((p[n] % 10 < d[n]) * (n - L + 1)));
    }
    int main()
    {
    	int t;
    	cin >> t;
    	while (t--)
    	{
    		int n;
    		scanf("%d", &n);
    		ll pseed, pa, pb, pmod, dseed, da, db, dmod;
    		scanf("%lld%lld%lld%lld%lld%lld%lld%lld", &pseed, &pa, &pb, &pmod, &dseed, &da, &db, &dmod);
    		for (int i = 0; i < n; i++)//构造p
    		{
    			p[i] = i;
    		}
    		ll seed = dseed;
    		for (int i = 0; i < n; i++)
    		{
    			d[i] = seed % 10;
    			seed = (seed * da + db) % dmod;
    		}
    		seed = pseed;
    		for (int i = 1; i < n; i++)
    		{
    			swap(p[seed % (i + 1)], p[i]);
    			seed = (seed * pa + pb) % pmod;
    		}
    		for (int i = 0; i < n; i++)
    		{
    			if (p[i] % 10 == d[i])p[i] = inf;//如果p[i]位置的字符就是d[i]的话,此次修改无意义,赋值p[i]为极大值
    		}
    		Cartesian(n);
    		memset(rk, 0, sizeof(int) * (n + 1));
    		dfs(st[1], 0, n, 0);
    		ll ans = 0, tmp = 1;
    		for (int i = 0; i <= n; i++)
    		{
    			ans = (ans + (ll)rk[i] * tmp % mod) % mod;
    			tmp *= 10000019LL;
    			tmp %= mod;
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    L.Problem L is the Only Lovely Problem(签到)

    题目描述

      给一个字符串,判断开头六个字符是不是 (lovely)(不区分大小写)。

    分析

      签到。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
    	string str;
    	cin >> str;
    	for (int i = 0; i < str.size(); i++)
    	{
    		str[i] = tolower(str[i]);
    	}
    	if (str.substr(0, 6) == "lovely")puts("lovely");
    	else puts("ugly");
    	return 0;
    }
    
  • 相关阅读:
    富文本
    管理员状态
    分页
    tp。3.2中的模板基础
    get和post之间的区别
    RegExp
    获取各种类型的节点
    节点的层次关系
    创建元素节点
    JavaScript 正则
  • 原文地址:https://www.cnblogs.com/ResuscitatedHope/p/13775506.html
Copyright © 2011-2022 走看看