• 2022牛客寒假算法基础集训营3 ABCDEGIL


    A. 智乃的Hello XXXX

    随便输出Hello xxx即可。

    B. 智乃买瓜

    链接:https://ac.nowcoder.com/acm/contest/23478/B
    来源:牛客网

    题目描述

    有一人前来买瓜。
    “哥们儿,这瓜多少钱一斤呐”
    “两块钱一斤”
    “What's up,这瓜皮是金子做的,还是瓜粒子是金子做的”
    

    智乃来到水果摊前买瓜,水果摊上贩卖着NN个不同的西瓜,第ii个西瓜的重量为wiwi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi22wi。

    也就是说对于每个西瓜,智乃都有三种不同的决策:

    1. 购买一整个重量为wiwi的西瓜
    2. 把瓜劈开,购买半个重量为wi22wi的西瓜
    3. 不进行购买操作

    为了简化题目,我们保证所有瓜的重量都是一个正偶数。

    现在智乃想要知道,如果他想要购买西瓜的重量和分别为k=1,2,3...Mk=1,2,3...M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对109+7109+7取余数后的结果。

    输入描述:

    第一行输入两个整数N,M(0≤N≤103,1≤M≤103)N,M(0≤N≤103,1≤M≤103),分别表示西瓜的数目NN,以及查询的重量上限为MM。
    若NN不为00,接下来一行NN个正偶数wi(2≤wi≤2×103)wi(2≤wi≤2×103)表示每个西瓜的重量。
    

    输出描述:

    输出一行MM个数字,分别表示购买西瓜的重量和为k=1,2,3...Mk=1,2,3...M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对109+7109+7取余数后的结果。
    

    示例1

    输入

    复制

    3 6
    8 2 4
    

    输出

    复制

    1 2 1 3 2 3
    

    比较裸的背包,dp[i, j]表示用前i个西瓜凑出j这个重量的方案数。需要注意的是dp数组第二维要开2e3,虽然m不超过1e3,但在代码中dp[i, w[i]] += 1;这一句涉及了wi,而wi可能达到2e3。转移方程见代码:

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define int long long
    #define mod 1000000007
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    ll dp[1005][2005] = { 0 };
    ll w[2005];
    ll n, m;
    signed main() {
    	cin >> n >> m;
    	for(ll i = 1; i <= n; i++) {
    		cin >> w[i];
    	}
    	for(ll i = 1; i <= n; i++) {
    		dp[i][w[i]] += 1;
    		dp[i][w[i] / 2] += 1;
    		for(int j = 1; j <= m; j++) {
    			dp[i][j] = (dp[i - 1][j] + dp[i][j]) % mod;
    			if(j - w[i] >= 0) dp[i][j] = (dp[i - 1][j - w[i]] + dp[i][j]) % mod;
    			if(j - w[i] / 2 >= 0) dp[i][j] = (dp[i - 1][j - w[i] / 2] + dp[i][j]) % mod;
    		}
    	}
    	for(int i = 1; i <= m; i++) {
    		cout << dp[n][i] << " ";
    	}
    	return 0;
    }
    

    C. 智乃买瓜(another version)

    链接:https://ac.nowcoder.com/acm/contest/23478/C
    来源:牛客网

    题目描述

    本题是原*版*的另一个版本,简单来讲,本题是原版输入输出的倒置。

    有一人前来买瓜。
    “哥们儿,这瓜多少钱一斤呐”
    “两块钱一斤”
    “What's up,这瓜皮是金子做的,还是瓜粒子是金子做的”
    

    智乃来到水果摊前买瓜,水果摊上贩卖着若干个不同的西瓜,第ii个西瓜的重量为wiwi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi22wi。

    也就是说对于每个西瓜,智乃都有三种不同的决策:

    1. 购买一整个重量为wiwi的西瓜
    2. 把瓜劈开,购买半个重量为wi22wi的西瓜
    3. 不进行购买操作

    为了简化题目,我们保证所有瓜的重量都是一个正偶数。

    现在智乃知道,购买西瓜的重量和分别为k=1,2,3...Mk=1,2,3...M时,购买西瓜的方案的种类数对109+7109+7取余数后的结果。

    她想要还原水果摊贩卖的这若干个不同的西瓜重量分别为多少,请你构造一个贩卖NN个不同西瓜的水果摊,其购买西瓜的重量和满足智乃的要求,当出现有多种符合题目描述的答案时,你只用输出任意一种。

    我们保证输入的数据至少存在一个N≤103N≤103的合法解。

    输入描述:

    第一行输入一个正整数M(1≤M≤103)M(1≤M≤103),表示智乃所知购买西瓜质量和的上限。
    接下来一行MM个整数,第ii个整数表示购买西瓜的重量和为ii时,购买西瓜的方案的种类数对109+7109+7取余数后的结果。
    

    输出描述:

    首先输出一个整数NN,西瓜的数目。要求你给出的NN大小范围在[0,103][0,103]之间
    接下来一行NN个正偶数wiwi,表示每个每个西瓜的重量。要求你给出的wiwi大小范围在[2,2×103][2,2×103]之间,且wiwi为偶数。
    
    
    输入数据保证,至少存在一组在输出范围内的合法解。
    

    示例1

    输入

    复制

    6
    1 2 1 3 2 3
    

    输出

    复制

    3
    8 2 4
    

    比赛时没有出思路,dp还是要多练~~~

    首先容易知道2的个数是确定的。不放倒着考虑,在B题中dp[i, j]的方案数是怎么得到的,这里就怎么减去。思路就是在把一个个dp方案数变为0的过程中不断将使用的重量放到答案vector中。从前往后遍历dp数组,如果一个位置的值不为0,说明这些剩下的这个重量i对应的方案数只能由i*2这个西瓜/2来提供。此时就把一个i*2放入ans,然后更新后面的dp数组部分。

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define mod 1000000007
    #define int long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    int m, dp[1005], w[1005];
    int c[2005] = { 0 };
    int cnt = 0;
    signed main() {
    	cin >> m;
    	for(int i = 1; i <= m; i++) {
    		cin >> dp[i];
    	}
    	vector<int> ans;
    	dp[0] = 1;//不要忘记
    	for(int i = 1; i <= m; i++) {
    		while(dp[i]) {
    			ans.push_back(i * 2);
    			for(int j = i; j <= m; j++) {//i==j时就是处理dp[i],剩下的方案数由dp[i]个i*2的瓜提供,因此dp[0]要设置为1,即每次都要减1
    				dp[j] = (dp[j] - dp[j - i] + mod) % mod;
    				if(j - i * 2 >= 0) dp[j] = (dp[j] - dp[j - i * 2] + mod) % mod;
    			}
    		}
    	}
    	cout << ans.size() << endl;
    	for(auto x : ans) {
    		cout << x << " ";
    	}
    	return 0;
    }
    

    D. 智乃的01串打乱

    纯签,随便选两个01位置交换即可。

    E. 智乃的数字积木

    链接:https://ac.nowcoder.com/acm/contest/23478/E
    来源:牛客网

    题目描述

    本题有对应的hard version,两者的区别仅在数据范围,保证easy version的测试用例集是hard version的子集。

    智乃酱有NN块积木,每一块积木都有自己的颜色以及数字,这NN块积木颜色的范围从11到MM,数字的范围从00到99。
    现在智乃酱把这些积木从左到右排成一排,这样积木看上去就形成了一个大整数,智乃觉得这个数字不够大,所以他决定交换一些积木使得这个大整数尽可能的大。

    具体来讲,智乃可以在任意时刻无限次的交换相邻且同色的数字积木。
    但是即使这样,智乃觉得这个数字还是不够大。

    所以智乃酱拿出了她的油漆桶,她决定进行KK次操作,每次操作都选中两种颜色PP,QQ,然后将所有颜色为PP的积木染成颜色QQ。
    当然,在染色结束后智乃酱也是可以交换相邻同色积木进行调整。

    现在智乃想要知道,她进行KK次染色操作之前,以及每次染色后能够通过交换得到最大的正整数是多少,当然啦,这个大整数被允许包含前导零。
    因为这个大整数很大,所以只要求你输出答案对109+7109+7取余数的结果即可。

    输入描述:

    第一行输入三个整数N,M,K(1≤N,M≤105,0≤K≤10)N,M,K(1≤N,M≤105,0≤K≤10)分别表示积木的数目,初始颜色的种类,以及智乃酱染色的次数。
    接下来输入一个长度为NN的字符串,字符串仅由数字0−90−9组成。
    接下来一行NN个正整数coli(1≤coli≤M)coli(1≤coli≤M)表示积木的颜色。
    接下来KK行,每行输入两个正整数P,Q(1≤P,Q≤M)P,Q(1≤P,Q≤M)表示选中两种颜色PP,QQ,然后将所有颜色为PP的积木染成颜色QQ。
    

    输出描述:

    输出K+1K+1行,即她进行KK次染色操作之前,以及每次染色后能够通过交换得到最大的正整数对109+7109+7取余数的结果。
    

    示例1

    输入

    复制

    10 2 0
    9586547120
    1 2 1 2 1 1 1 1 1 1
    

    输出

    复制

    586754147
    

    示例2

    输入

    复制

    10 2 1
    9586547120
    1 2 1 2 1 1 1 1 1 1
    1 2
    

    输出

    复制

    586754147
    876554147
    

    示例3

    输入

    复制

    5 5 4
    12345
    1 2 3 4 5
    3 2
    2 5
    4 5
    5 1
    

    输出

    复制

    12345
    13245
    13245
    15432
    54321
    

    注意到k范围很小,因此每次染色直接暴力处理,然后对于同色的区间暴力排序,再暴力遍历计算字符串的值即可。

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define mod 1000000007
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    int n, m, k;
    char s[100005];
    int c[100005];
    bool cmp(char a, char b) {
    	return a > b;
    }
    int main() {
    	cin >> n >> m >> k;
    	c[n + 1] = -1;
    	scanf("%s", s + 1);
    	
    	for(int i = 1; i <= n; i++) {
    		cin >> c[i];
    	}
    	int lst = 1;
    	for(int i = 2; i <= n + 1; i++) {
    		if(c[i] != c[i - 1]) {
    			sort(s + lst, s + (i - 1) + 1, cmp);
    			lst = i;
    		}
    	}
    	ll ans = 0;
    	for(int i = 1; i <= n; i++) {
    		ans = (ans * 10) % mod;
    		ans = (ans + s[i] - '0') % mod;
    	}
    	cout << ans << endl;
    	for(int i = 1; i <= k; i++) {
    		int p, q;
    		cin >> p >> q;
    		for(int j = 1; j <= n; j++) {
    			if(c[j] == p) c[j] = q;
    		}
    		int lst = 1;
    		for(int j = 2; j <= n + 1; j++) {
    			if(c[j] != c[j - 1]) {
    				sort(s + lst, s + (j - 1) + 1, cmp);
    				lst = j;
    			}
    		}
    		ans = 0;
    		for(int i = 1; i <= n; i++) {
    			ans = (ans * 10) % mod;
    			ans = (ans + s[i] - '0') % mod;
    		}
    		cout << ans << endl;
    	}
    	return 0;
    }
    

    G. 智乃的树旋转

    链接:https://ac.nowcoder.com/acm/contest/23478/G
    来源:牛客网

    题目描述

    本题有对应的hard version, *easy version有额外的限制条件*,保证easy version的测试用例集是hard version的子集。

    树旋转是在二叉树中的一种子树调整操作, 每一次旋转并不影响对该二叉树进行中序遍历的结果。

    树旋转通常应用于需要调整树的局部平衡性的场合。树旋转包括两个不同的方式,分别是左旋和右旋。 两种旋转呈镜像,而且互为逆操作。

    img

    如图所示,为树的左旋转和右旋转示意图。当AA节点位于BB节点的左子树时,可以进行以AA节点为旋转轴的右旋转,反之可以进行以BB节点为旋转轴的左旋转。

    具体来讲

    右旋转:

    当节点AA位于节点BB的左孩子时,节点AA可作为旋转轴进行右旋转,该旋转操作涉及到结构改变的节点有44个。
    我们记旋转前AA节点的右孩子为ββ节点,BB节点的父节点为XX节点(若BB节点就是整棵树的根节点,则无需做与XX节点相关的操作)。
    则旋转后ββ节点由AA节点的右孩子变为BB节点的左孩子,XX节点的其中一个孩子节点由BB节点改变为AA节点,并且它们和XX节点的中序顺序不发生其他变化;也就是说原来BB节点是XX节点的右孩子,则旋转后变为AA节点替换BB节点成为XX节点新的右孩子,反之若BB节点初始是XX节点的左孩子,则旋转后AA节点替换BB节点成为XX节点新的左孩子。

    左旋转:
    当节点BB位于节点AA的右孩子时,节点BB可作为旋转轴进行左旋转,该旋转操作涉及到结构改变的节点有44个。
    我们记旋转前BB节点的左孩子为ββ节点,AA节点的父节点为XX节点(若AA节点就是整棵树的根节点,则无需做与XX节点相关的操作)。
    则旋转后ββ节点由BB节点的左孩子变为AA节点的右孩子,XX节点的其中一个孩子节点由AA节点改变为BB节点,并且它们和XX节点的中序顺序不发生其他变化;也就是说原来AA节点是XX节点的右孩子,则旋转后变为BB节点替换AA节点成为XX节点新的右孩子,反之若AA节点初始是XX节点的左孩子,则旋转后BB节点替换AA节点成为XX节点新的左孩子。

    智乃最近学习了树旋转,树旋转的本质是二叉树旋转轴节点与其父节点父子关系的改变,从视觉效果上看起来好像整个树进行了“旋转”。

    在实际的操作过程中,如果指定旋转轴,其实就没有所谓“左旋转”和“右旋转”的区别,当旋转轴确定时,将只有一种旋转方法,遵循以下规则。

    • 根节点无法作为旋转轴被选定。
    • 当旋转轴是其父节点的左子树时,旋转轴只能进行右旋转。
    • 当旋转轴是其父节点的右子树时,旋转轴只能进行左旋转。

    现在智乃有一颗尺寸大小为NN二叉树,智乃对其做了一次旋转操作将其打乱,她想让你通过一系列树的旋转操作将其还原,要求你的操作次数不多于N2N2次。

    输入描述:

    第一行输入一个正整数N(1≤N≤103)N(1≤N≤103),表示二叉树的节点数目,节点从11到NN标号。
    
    
    接下来NN行输入二叉树一开始的样子。
    每行输入两个非负整数lch,rch(0≤lch,rch≤N)lch,rch(0≤lch,rch≤N)表示每个节点的左右子树。
    当lchlch的值为00时,则表示该节点无左子树,当rchrch的值为00时,则表示该节点无右子树。
    
    
    
    接下来NN行输入二叉树被打乱后的样子。
    每行输入两个非负整数lch,rch(0≤lch,rch≤N)lch,rch(0≤lch,rch≤N)表示每个节点的左右子树。
    当lchlch的值为00时,则表示该节点无左子树,当rchrch的值为00时,则表示该节点无右子树。
    
    
    要求你将打乱后的二叉树通过一系列旋转操作还原
    

    输出描述:

    首先输出一个整数KK,表示你还原二叉树进行旋转的次数,要求你给出KK的范围在[0,N2][0,N2],接下来KK行,依次输出旋转操作的旋转轴。
    由于旋转轴只能进行左旋转或者右旋转其中的一种,裁判程序将会自己判断当前需要进行的是左旋转还是右旋转。
    注意:旋转过程中的根节点无法作为旋转轴进行旋转,如果你指定旋转过程中的根节点作为旋转轴,则裁判程序将直接给出WA的结果。
    
    
    easy version额外限制条件,保证可以通过不多于一次树旋转的操作还原二叉树,即一定存在K≤1K≤1的解。
    

    示例1

    输入

    复制

    5
    2 3
    0 0
    4 5
    0 0
    0 0
    2 4
    0 0
    1 5
    0 0
    0 0
    

    输出

    复制

    1
    1
    

    easy version比较简单,因为最多只旋转一次,因此旋转的只能是前后两棵树之间父子关系互换的两个点。

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    int n;
    struct node {
    	int ls, rs;
    } n1[1005], n2[1005];
    int fa1[1005], fa2[1005];
    int main() {
    	cin >> n;
    	for(int i = 1; i <= n; i++) {
    		int l, r;
    		cin >> l >> r;
    		n1[i].ls = l, n1[i].rs = r;
    		fa1[l] = fa1[r] = i;
    	}
    	for(int i = 1; i <= n; i++) {
    		int l, r;
    		cin >> l >> r;
    		n2[i].ls = l, n2[i].rs = r;
    		fa2[l] = fa2[r] = i;
    	}
    	int p1 = 0, p2 = 0;
    	for(int i = 1; i <= n; i++) {
    		if(n1[i].ls != n2[i].ls || n1[i].rs != n2[i].rs) {
    			if(!p1) p1 = i;
    			else p2 = i;
    		}
    	}
    	if(!p1 && !p2) {
    		puts("0");
    		return 0;
    	}
    	puts("1");
    	int ans = 0;
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= n; j++) {
    			if(i != j && fa1[i] == j && fa2[j] == i) {
    				ans = j;
    				break;
    			}
    		}
    	}
    	cout << ans << endl;
    	return 0;
    }
    

    I. 智乃的密码

    链接:https://ac.nowcoder.com/acm/contest/23478/I
    来源:牛客网

    题目描述

    智乃去注册账号,他发现网站的的密码必须符合以下几个条件

    • 密码是仅包含大小写英文字母、数字、特殊符号的字符串。
    • 密码的长度不少于LL个字符,并且不多于RR个字符。
    • 密码中应该至少包括①大写英文字母、②小写英文字母、③数字、④特殊符号这四类字符中的三种。

    所谓特殊字符,是指非大小写字母、数字以及空格回车等不可见字符的可见字符,包括但不限于"~!@#$%^&*()_+"。

    现在智乃有一个长度大小为NN的字符串SS,她想知道SS串中有多少个子串是一个符合条件的密码,请你帮助智乃统计符合条件的密码数目。

    子串是指字符串中某一段连续的区间,例如对于字符串"abcde"来说,"abc","cde"都是它的子串,而"ace"不是它的子串。

    输入描述:

    第一行输入三个正整数N,L,R(1≤N≤105,1≤L≤R≤N)N,L,R(1≤N≤105,1≤L≤R≤N),表示SS串的长度,合法密码长度应该在LL到RR个字符之间。
    接下来一行输入一个长度为NN的字符串SS,字符串仅包括①大写英文字母、②小写英文字母、③数字、④特殊符号四类字符。
    

    输出描述:

    仅一行一个整数,表示有多少子串是一个合法的密码。
    

    示例1

    输入

    复制

    10 6 8
    asdfeg111*
    

    输出

    复制

    3
    

    说明

    "eg111*","feg111*","dfeg111*"
    

    这个题双指针或二分都可以做,比赛时写了半天错的双指针改不出来,没办法只能二分了...

    二分时遍历每个可能的右端点,二分的是实际区间右端点到左端点的距离(因为距离小的时候满足,则距离大的时候一定满足,解满足单调性),判断区间是否合法可以用前缀和。举例来说,设有区间1 2 3 4 5 6,L = 3, R = 6, 如果[1 2 3 4]合法,那么[1 2 3 4 5]和[1 2 3 4 5 6]肯定都合法。

    注意左端点不会超过1!一定要特别处理!代码比较抽象,简单参考一下吧~

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define int long long
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    int n, L, R;
    char s[200005];
    int sum[200005][4] = { 0 };
    bool check(int mid, int i) {
    	int a = sum[i][0] - sum[mid - 1][0];
    	if(a) a = 1;
    	int b = sum[i][1] - sum[mid - 1][1];
    	if(b) b = 1;
    	int c = sum[i][2] - sum[mid - 1][2];
    	if(c) c = 1;
    	int d = sum[i][3] - sum[mid - 1][3];
    	if(d) d = 1;
    	if(a + b + c + d >= 3) return 1;
    	else return 0;
    }
    signed main() {
    	cin >> n >> L >> R;
    	scanf("%s", s + 1);
    	int digit = 0, upper = 0, lower = 0, spe = 0;
    	int lft = 1;
    	int ans = 0;
    	int cnt = 0;
    	for(int i = 1; i <= n; i++) {
    		for(int j = 0; j < 4; j++) {
    			sum[i][j] = sum[i - 1][j];
    		}
    		if(s[i] >= '0' && s[i] <= '9') sum[i][0]++;
            else if(s[i] >= 'a' && s[i] <= 'z') sum[i][1]++;
            else if(s[i] >= 'A' && s[i] <= 'Z') sum[i][2]++;
            else sum[i][3]++;
            if(i < L) continue;
            int l = 0, r = min(R, i) - L, mid;
            while(l < r) {
            	mid = (l + r) >> 1;
            	if(check(i - (mid + L) + 1, i)) r = mid;
            	else l = mid + 1;
            }
            if(check(i - (l + L) + 1, i)) ans += (min(R, i) - (L + l) + 1);//
    	}
    	cout << ans;
    	return 0;
    }
    // 10 3 5
    // a1******1a
    
    // 5 5 5
    // a*111
    

    L. 智乃的数据库

    链接:https://ac.nowcoder.com/acm/contest/23478/L
    来源:牛客网

    题目描述

    智乃最近在学习数据库的查询语言SQL。SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括数据插入、查询、更新和删除,数据库模式创建和修改,以及数据访问控制。

    使用SQL,可以灵活的对数据库表进行操作,在数据库的查询语句中有"GROUP BY"这样一种关键字。

    GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。

    例如数据库中储存了这样一个数据库表Table,执行带有"GROUP BY"关键字的查询语句。

    id name val 1 chino 1 2 chino
    2 3 kokoa
    3 SELECT COUNT(*) FROM Table GROUP BY name;

    SQL语句中COUNT表示聚合后每一个组中有多少条数据。

    当执行上述的SQL查询语句后,其返回的结果如下

    2 2 1第一行的2表示按照name字段进行分组聚合,一共可以分出2组。

    第二行的两个整数表示每组中各有多少条数据。

    当然了,"GROUP BY"关键字是可以选中多个列进行分组聚合的,只需要把这些字段用逗号隔开即可。

    SELECT COUNT(*) FROM Table GROUP BY name,val;例如这样的SQL查询语句,执行后返回的结果如下

    3 1 1 1现在智乃把这张数据库表导出给你,请你执行一个SELECT COUNT(*) FROM Table GROUP BY ...;的查询语句,请你把查询的结果告诉智乃。

    为了简化问题,我们假设数据库的表由NN条记录以及MM个字段组成,且这些字段的类型均为int类型。

    输入描述:

    第一行输入两个正整数N,M(1≤N,M≤1000)N,M(1≤N,M≤1000),表示数据库表中有NN条记录以及MM个字段。
    
    
    接下来一行输入MM个字符串Si(1≤∣Si∣≤50)Si(1≤∣Si∣≤50)表示每个字段的名称。
    
    
    接下来NN行,每行MM列,输入一个二维表格datai,j(0≤datai,j≤109)datai,j(0≤datai,j≤109)表示数据库表中第ii条记录的第jj个字段的值。
    
    
    接下来输入一行一个字符串SQL(1≤∣SQL∣≤5×104)SQL(1≤∣SQL∣≤5×104),表示查询语句,查询语句的格式为"SELECT    COUNT(∗)    FROM    Table    GROUP    BY    ...;""SELECTCOUNT(∗)FROMTableGROUPBY...;"。
    
    
    其中"...""..."是若干个字段名称,保证是之前输入的MM个字段中的某些字段,且这若干个字段互不相同,字段与字段之间用一个逗号隔开。
    

    输出描述:

    第一行输出一个整数KK表示聚合后组的数目。
    接下来一行输出KK个整数,表示每组中聚合的记录数目,整数与整数之间用空格隔开。
    
    
    你可以按照你喜欢的顺序输出答案,裁判程序将忽略输出顺序的影响。
    

    示例1

    输入

    复制

    4 3
    id name val
    1 1 2
    1 1 3
    1 2 1
    2 2 2
    SELECT COUNT(*) FROM Table GROUP BY name,id;
    

    输出

    复制

    3
    1 2 1
    

    模拟题。思路就是按这若干个字段进行排序,然后相邻的这若干个字段值相同的分到一组即可。

    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <vector>
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    using namespace std;
    struct node {
    	int data[1005];
    } p[1005];
    int rela[1005], cnt = 0;
    bool cmp(node a, node b) {
    	for(int i = 1; i <= cnt; i++) {
    		if(a.data[rela[i]] == b.data[rela[i]]) continue;
    		else return a.data[rela[i]] < b.data[rela[i]];
    	}
    	return a.data[rela[cnt]] < b.data[rela[cnt]];
    }
    int n, m;
    string s[1005];
    string sen;
    map<string, int> mp;
    int main() {
    	cin >> n >> m;
    	for(int i = 1; i <= m; i++) {
    		cin >> s[i];
    		mp[s[i]] = i;
    	}
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= m; j++) {
    			cin >> p[i].data[j];
    		}
    	}
    	getchar();
    	getline(cin, sen);
    	int pos = sen.find("BY");
    	pos += 3;
    	int lst = pos;
    	for(int i = pos; i < sen.size(); i++) {
    		if(sen[i] == ',' || sen[i] == ';') {
    			string tmp = sen.substr(lst, i - lst);
    			lst = i + 1;
    			cnt++;
    			rela[cnt] = mp[tmp];
    		}
    	}
    	sort(p + 1, p + n + 1, cmp);
    	int num = 1;
    	vector<int> ans;
    	for(int i = 2; i <= n + 1; i++) {
    		bool diff = 0;
    		for(int j = 1; j <= cnt; j++) {
    			if(p[i].data[rela[j]] != p[i - 1].data[rela[j]]) {
    				diff = 1;
    				break;
    			}
    		}
    		if(diff) {
    			ans.push_back(num);
    			num = 1;
    		} else {
    			num++;
    		}
    	}
    	cout << ans.size() << endl;
    	for(int i = 0; i < ans.size() - 1; i++) {
    		cout << ans[i] << " ";
    	}
    	cout << ans[ans.size() - 1];
    	return 0;
    }
    
  • 相关阅读:
    树的直径
    POJ3264 Balanced Lineup
    [mock]10月11日
    POJ1062 昂贵的聘礼
    POJ3295 Tautology
    [topcoder]TopographicalImage
    POJ1753 Flip Game
    [leetcode]Copy List with Random Pointer
    [leetcode]Candy
    [leetcode]Gas Station
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/15854172.html
Copyright © 2020-2023  润新知