小红的彩带
小红有一条长度为 n 的彩带,彩带的每一个位置都有一个颜色,用 ai表示。
小红每次会从左往后或从右往左剪一段长度为 x 的彩带,她想知道她每次剪下来的彩带有多少种颜色。
输入描述:
第1行输入2个正整数n,q(1<=n,q<=105),n表示彩带长度,q表示剪彩带次数
第2行输入n个正整数ai(1<=ai<=109)表示彩带每一个位置的颜色
接下来q行,每行先输入1个字符c,c为'L'
说明从左到右,c为'R'
从右往左剪,再输入1个正整数x(1<=x<=n).
保证x的总和不超过n
输出描述:
输出q行,每行输出一个整数表示答案
示例:
输入:
6 3
1 1 4 5 1 4
L 3
R 2
L 1
输出:
2
2
1
解题思路:
- 将彩带看作线段,设两个变量
l
和r
,作为线段的两端,那一端被剪,就对那一端加或减 - 比如c为’L’,x=3时,那么l=l+3,r同理
- 想知道剪下来的有多少种颜色,也就是有多少个不同的数,也就是剪下来的那段线段,去重之后的数组长度,就是答案
参考代码:
C++:
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> colors(n);//存储彩带n个位置的颜色
for (int i = 0; i < n; ++i) {
cin >> colors[i];
}
int l=0,r=n;//剪去彩带后的边界,
for (int i = 0; i < q; ++i) {//q次剪彩带
char c;
int x;
cin >> c >> x;
set<int> cut_colors;//这个set容器在这就格外好用了,自动去重省不少事
if (c == 'L') {
for (int j = l; j < l+x&&j<r; ++j) {
cut_colors.insert(colors[j]);
}
l+=x;
} else {
for (int j = r-1; j >= r-x&&j>=l; --j) {
cut_colors.insert(colors[j]);
}
r-=x;
}
cout << cut_colors.size() << endl;//输出剪下线段的数组长度
}
return 0;
}
用C,要自己写个数组去重,其他的没什么区别
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef long long ll;
int removeDuplicates(ll arry[], int size) {
if (size == 0) {
return 0;
}
int low = 1, high = size - 1;
while (low <= high) {//就是每次遍历0到low这一段,遍历到其中重复的元素,往最后面扔
for (int i = 0; i < low; i++) {
if (arry[i] == arry[low]) {
int var = arry[high];
arry[high] = arry[low];
arry[low] = var;
high--;
low--;
break;
}
}
low++;
}
return high + 1;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
ll a[n];
for (int i = 0; i < n; i++)
scanf("%lld", &a[i]);
int l = 0, r = n;
while (q--) {
char c;
int x;
scanf(" %c %d", &c, &x);
ll b[x];
memset(b, 0, sizeof(b));
int size = 0;
if (c == 'L') {
for (int i = l; i < l + x && i < r; i++)
b[size++] = a[i];
l += x;
} else {
for (int i = r - 1; i >= r - x && i >= l; i--) {
b[size++] = a[i];
}
r -= x;
}
size = removeDuplicates(b, size);//返回数组去重之后的数组长度
printf("%d\n", size);
}
return 0;
}
祖玛游戏
小红喜欢玩祖玛游戏,游戏规则如下:
- 游戏中会有一条路径,一群彩色的球沿着这条路径滚动前进。
- 玩家发射出彩色的球,使得发射的球射入滚动的球中。
- 如果有 3 个或更多数量、颜色相同的球连在一起时,这些球就会被消除。
现在小红有一个长度为n的数组,数组中的每个元素代表一种颜色的球,小红有一次机会调整数组中球的顺序,问最少需要添加多少个球可以消除所有球。
输入描述:
第一行一个整数n,表示数组的长度。
第二行n个整数ai,表示每个球的颜色。
1<=n<=105
1<=ai<=109
输出描述:
输出一个整数,表示最少需要添加多少个球可以消除所有球。
示例:
输入:
5
1 2 2 3 1
输出:
4
说明:
重新排列成[1,1,2,2,3],这样再添加1个1号球1个2号球和2个3号球即可消除所有球。
解题思路:
这题主要是可能理解错题目,游戏规则的第三条意思是有3个球或以上连在一起就会自动消失了,不需要再打进一个球
这题理解题意就很简单了,其他的就不用说了
参考代码:
C++:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int n;
cin>>n;
ll a[n];
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);//排序,也就是调整组中球位置,方便把相同的球汇聚在一起
int count=0;
map<ll,int>sum;//first是球的颜色,second是球的数量
for(int i=0;i<n;i++)
{
sum[a[i]]++;//统计出每种颜色球的数量
}
for(auto m:sum)//遍历map
{
int x=m.second;
if(x<3)//当球的数量小于3,就要打进一定量的球,让球的数量等于3
{
count+=3-x;
}
}
cout<<count;
return 0;
}
C:
#include <stdio.h>
typedef long long ll;
int n;
void swap(ll *x,ll *y)
{
ll t=*x;
*x=*y;
*y=t;
}
int partition(ll a[],int low,int high)
{
ll pivot=a[high];
int i=low-1;
for(int j=low;j<high;j++)
{
if(a[j]<pivot)
{
i++;
swap(&a[j],&a[i]);
}
}
swap(&a[i+1],&a[high]);
return i+1;
}
void quickSort(ll a[],int low,int high)
{
if(low<high)
{
int pi=partition(a,low,high);
quickSort(a,low,pi-1);
quickSort(a, pi+1, high);
}
}
int main() {
scanf("%d", &n);
ll a[n];
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
quickSort(a,0,n-1);//快速排序
int count=0;
for(int i=0;i<n;i++)//排序后,再数相同的数就容易多了
{
int count_1=1;
if(a[i]==a[i+1]&&i<n-1)
{
while(a[i]==a[i+1]&&i<n-1)
{
count_1++;
i++;
}
}
if(count_1<3)
count+=3-count_1;
}
printf("%d",count);
return 0;
}
小红的数组构造
小红希望你构造一个长度为n的数组,满足所有元素之和等于x,并且所有元素的按位或恰好等于y。
输入描述:
三个正整数n,x,y,用空格隔开。
1<=n<=104
1<=x,y<=109
输出描述:
如果无解,请输出-1
否则输出n个非负整数,代表小红构造的数组。如果有多解,输出任意一个即可。
示例:
输入:
4 5 2
输出:
-1
解题思路:
- 先要理解所有元素按位或的意思,就是
a[0]|a[1]|a[2]
,就是构造的数组元素互相或最后的结果要为y, - 这也就明白了一点,数组里每一个元素都小于或等于y,而且x必定大于y才会才会存在这个数组,举个例子,如果y是7,要想数组元素按位或的结果等于y,7的二进制为0111,数组元素至少要为1,2,4,或有个7
- 提取出y的二进制位中的为1的值,拼凑数组,保证最终结果等于y
参考代码:
C++:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll x, y;
int a[50] = {0};
ll b[50] = {0};
int count1, count2 = 0;
void fun() {
if (x < y) {//如果x<y,数组不存在
cout << -1;
return;
}
count1 = 0;//y的二进制位数
count2 = 0;//y的二进制中为1的数
ll z=y;
while (z) { //取出y的二进制
a[count1++] = z % 2;
z >>= 1;
}
for (int i = 0; i < count1; i++) { //提出y的二进制为1的值
if (a[i] == 1) {
b[count2++] = pow(2, i);
}
}
vector<ll>ans(n, 0);//最终构造数组
ans[0]=y;
x-=y;
for(int i=count2-1;i>=0;i--)//保证能在n个元素之和等于x,从最大的开始遍历递减
{
for(int j=1;j<n;j++)//构造数组
{
if(x>=b[i])
{
ans[j]+=b[i];
x-=b[i];
}
}
}
if (x != 0) {//保证结果所有元素之和符合等于x
cout << -1;
return;
}
for (int i = 0; i < n; i++)
cout << ans[i] << ' ';
}
int main() {
cin >> n;
cin >> x >> y;
fun();
return 0;
}
优化一下,上面用来两个数组,y转二进制还有些麻烦
C:
#include <stdio.h>
typedef long long ll;
ll qpow(int x,ll y)//快速幂,写完后又不想用了╰( ̄ω ̄o)
{
ll res=1;
while(y)
{
if(y&1)
res*=x;
x*=x;
y>>=1;
}
return res;
}
int main() {
int n;
ll x,y;
scanf("%d%lld%lld",&n,&x,&y);
if(y>x)
{
printf("-1");
return 0;
}
ll a[n];
memset(a,0,sizeof(a));
a[0]=y;
x-=y;
for(int i=32;i>=0;i--)//从高位到低位遍历y的二进制
{
if(y>>i&1)//当前遍历到的位为1时,对于每一位为1的位置,尝试将剩余的和x用该位置的值补充到数组中
for(int j=1;j<n;j++)
{
if(x>=(1<<i))
{
a[j]+=1<<i;
x-=1<<i;
}
}
}
if(x!=0)
{
printf("-1");
return 0;
}
for(int i=0;i<n;i++)
printf("%lld ",a[i]);
return 0;
}
小红的特殊数组
小红定义一个数组为特殊数组:这个数组大小至少为 3 ,只由 2 种数字组成,且其中一种数字恰好出现一次。
例如:[1,1,4],[1,4,1] 是特殊数组(长度为3,只有1和4组成,4恰好出现一次),[1,4] ,[5,1,4] ,[1,1,1] 都不是特殊数组。
小红有一个长度为 n 的数组 a ,她想知道这个数组有多少个子序列是特殊数组。
输入描述:
第1行输入1个正整数n(1<=n<=105),表示数组长度。
第2行输入n个正整数ai(1<=ai<=109)表示数组。
输出描述:
输出一个整数表示答案。由于答案可能很大,输出答案对109+7取模的结果
示例:
输入:
4
1 1 4 5
输出:
2
说明
子序列[1,1,4],[1,1,5]是特殊数组。
解题思路:
对于这道题,如果能想到组合数的话,就好做了
- 先统计出各个相同数字的数量,只关注有相同的数字,
- 这时候就能列出组合公式了,遍历数字,从n-当前数字相同数字数挑一个数进入该子序列,又因为位置的不同,会产生新序列,所以还要再开个for循环遍历相同数字数,i从2开始累加,进入该子序列,
- 也就是count=count+C(n-当前数字相同数字数)*C(当前数字相同数字数,i),遍历完所有有相同的数字后,就是结果
- 因为组合数涉及到了除法,最后的结果可能是分数,又要取模,为了保证结果的正确,要把除法变成逆元的形式
- 简要说下逆元:根据 逆元 的定义,如果你最后得到的答案是形如a/b的分数,之后你需要对𝑝 取模的话,你需要输出(a*bp-2) mod p来保证你的答案是正确的
参考代码:
C++:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll N=1e5;
ll cnt[100000],cnt1[100000];//cnt是阶乘,cnt1是逆元阶乘,用于方便计算组合数
ll qpow(ll a,ll b)//快速幂函数,处理逆元时用到
{
ll res=1;
while(b)
{
if(b&1)
res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll inverse(ll x,ll y)//计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
{
return cnt[x]*cnt1[y]%mod*cnt1[x-y]%mod;
}
int main() {
cnt[0]=1;
for(int i=1;i<N;i++)
{
cnt[i]=cnt[i-1]*i%mod;
}
cnt1[N-1]=qpow(cnt[N-1],mod-2)%mod;//cnt1[N-1]=1/cnt[N-1],但我们显然不能这样,就要逆元处理一下
for(int i=N-1;i>0;i--)
{
cnt1[i-1]=cnt1[i]*i%mod;//分母是在减小的,所以乘以i
}
int n;
cin>>n;
unordered_map<ll,ll>number;//用一个map数组把相同的数分列出来
for(int i=0;i<n;i++)
{
ll a;
cin>>a;
number[a]++;
}
ll count=0;//统计有多少个子序列符合
for(auto m:number)//遍历
{
if(m.second>=2)//只处理有相同数字的
{
for(int i=2;i<=m.second;i++)
/*子序列最少要有两个相同的数,然后从n-m.secnod也就是剩下不同的数里挑一个,
再从m.second中选出i个组成子序列,位置不同,也是算不同的序列
比如题目就给出了例子,[1,1,4]和[1,4,1]*/
{
count=(count+(n-m.second)%mod*inverse(m.second,i)%mod)%mod;
}
}
}
cout<<count;
return 0;
}
难绷,谁要用C写算法,头不得炸,对于极大的数去统计这个数相同的个数,对于C似乎麻烦,我第一时间想到的是先排序,再开个数组来统计
结果哪怕用快排也不行,超时了
C:
#include <stdio.h>
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 100000
ll cnt[100000], cnt1[100000];
ll number[100000];//number是统计各组相同数的数量
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
void swap(ll* a, ll* b) {
ll t = *a;
*a = *b;
*b = t;
}
int partition(ll a[], int low, int high) {
ll pivot = a[high];
int i = low - 1;
for (int j = low ; j < high; j++) {
if (a[j] < pivot) {
i++;
swap(&a[i], &a[j]);
}
}
swap(&a[i + 1], &a[high]);
return i + 1;
}
void quickSort(ll a[], int low, int high) {//快排
if (low < high) {
int pi = partition(a, low, high);
quickSort(a, low, pi - 1);
quickSort(a, pi + 1, high);
}
}
ll inverse(ll x, ll y) { //计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
return cnt[x] * cnt1[y] % mod * cnt1[x - y] % mod;
}
int main() {
cnt[0] = 1;
for (int i = 1; i < N; i++) {
cnt[i] = cnt[i - 1] * i % mod;
}
cnt1[N - 1] = qpow(cnt[N - 1], mod - 2) % mod;
for (int i = N - 1; i > 0; i--) {
cnt1[i - 1] = cnt1[i] * i % mod;
}
int n;
scanf("%d", &n);
ll a[n];
for (int i = 0; i < n; i++)
scanf("%lld", &a[i]);
quickSort(a, 0, n - 1);
int _count=0;
for(int i=0;i<n;i++)
{
number[_count]=1;
if(a[i]==a[i+1]&&i<n-1)
{
while(a[i]==a[i+1]&&i<n-1)
{
number[_count]++;
i++;
}
}
_count++;
}
ll count=0;
for(int i=0;i<_count;i++)
{
if(number[i]>=2)
{
int m=number[i];
for(int j=2;j<=m;j++)
{
count=(count+(n-m)%mod*inverse(m,j)%mod)%mod;
}
}
}
printf("%lld",count);
return 0;
}
斯,是我井底之蛙了,竟然还有qsort(),C也有堪比sort的函数,哈哈,这可比我写的快排快多了
简单说下:
- qsort 是 C 标准库中提供的一个函数,用于对数组进行快速排序。它在 <stdlib.h> 头文件中定义。qsort 使用的是快速排序算法(quicksort),这是一种高效的排序算法,平均时间复杂度为 O(n log n)。
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));
参数:
base: 指向待排序数组的第一个元素的指针。
nitems: 数组中的元素数量。
size: 数组中每个元素的大小(以字节为单位)。
compar: 比较函数的指针,该函数用于比较两个元素。比较函数应当返回一个整数,表示比较结果:
小于零:表示第一个元素小于第二个元素。
等于零:表示两个元素相等。
大于零:表示第一个元素大于第二个元素。
优化后能过的,其实就是不用我的快排,用qsort就行,不过还要写个cmp函数:
C:
#include <stdio.h>
#include<stdlib.h>
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 100000
ll cnt[100000], cnt1[100000];
ll number[100000];
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
void swap(ll* a, ll* b) {
ll t = *a;
*a = *b;
*b = t;
}
int partition(ll a[], int low, int high) {
ll pivot = a[high];
int i = low - 1;
for (int j = low ; j < high; j++) {
if (a[j] < pivot) {
i++;
swap(&a[i], &a[j]);
}
}
swap(&a[i + 1], &a[high]);
return i + 1;
}
void quickSort(ll a[], int low, int high) {
if (low < high) {
int pi = partition(a, low, high);
quickSort(a, low, pi - 1);
quickSort(a, pi + 1, high);
}
}
ll inverse(ll x, ll
y) { //计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
return cnt[x] * cnt1[y] % mod * cnt1[x - y] % mod;
}
int cmp(const void* a, const void* b) {//对于整型排序
return *(ll*)a - *(ll*)b;
}
int main() {
cnt[0] = 1;
for (int i = 1; i < N; i++) {
cnt[i] = cnt[i - 1] * i % mod;
}
cnt1[N - 1] = qpow(cnt[N - 1], mod - 2) % mod;
for (int i = N - 1; i > 0; i--) {
cnt1[i - 1] = cnt1[i] * i % mod;
}
int n;
scanf("%d", &n);
ll a[n];
for (int i = 0; i < n; i++)
scanf("%lld", &a[i]);
qsort(a, n, sizeof(ll), cmp);//好用,哈哈
int _count = 0;
for (int i = 0; i < n; i++) {
number[_count] = 1;
if (a[i] == a[i + 1] && i < n - 1) {
while (a[i] == a[i + 1] && i < n - 1) {
number[_count]++;
i++;
}
}
_count++;
}
ll count = 0;
for (int i = 0; i < _count; i++) {
if (number[i] >= 2) {
int m = number[i];
for (int j = 2; j <= m; j++) {
count = (count + (n - m) % mod * inverse(m, j) % mod) % mod;
}
}
}
printf("%lld", count);
return 0;
}