A. Dreamoon and Ranking Collection
题意:给一个数组ai给你,你从中数组中选一些数字凑成1~p的所有数字,允许有x个数没有的,意思就是如果x = 2,你可以拿1,3,5,但是可以当做你已经凑出了1 ~ 5的所有数字了,因为空出来的数只有2和4,只有两个数小于等于x,问p最大是多少?
这里因为数据范围太小了,最大才100,就直接写了个暴力
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
int n,x;
int ans = 0;
const int N= 105;
int a[N];
set<int> myset;
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&x);
int ans = x;
int mx = 0;
myset.clear();
for(int i = 1;i <= n;i++){
scanf("%d",a+i);
myset.insert(a[i]);
mx = max(mx,a[i]);
}
for(int i = 1;i <= mx+x;i++){
int cnt = 0;
for(int j = 1;j <= i;j++){
if(!myset.count(j)) cnt++;
}
if(cnt <= x) ans = max(ans,i);
}
printf("%d\n",ans);
}
return 0;
}
B. Dreamoon Likes Permutations
题意:给你一个n个元素的数组,你中间划一刀分开两半,要求一半是1~x的排列,另一半是1 ~ y的排列,问一共有几种切法,输出几种和各自的x和y
怎么判断一段是不是1 ~ x的排列,首先这里每个数字都只能出现一次,其次这段的和要是以1为首项,公差为1,项数位这段的长度的等差数列之和,所以先求个前缀和,其次记录一下出现了两次的数字的情况有几次,注意如果这个数组有一个数字出现了三次,是没有答案的;
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
vector<int> ans;
int n;
const int N = 2e5+10;
int cnt,a[N],num[N],num2[N];
ll sum[N];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void init(){
for(int i = 1;i <= n;i++){
num[i] = num2[i] = 0;
}
cnt = 0;
ans.clear();
}
int main(){
int t;scanf("%d",&t);
while(t--){
bool f = 0;
n = read();
init();
for(int i = 1;i <= n;i++){
a[i] = read();
sum[i] = (sum[i-1]+1ll*a[i]);
num[a[i]]++;
if (num[a[i]] == 2) cnt++;
else if(num[a[i]] > 2) f = 1;
}
if(f){puts("0");continue;}
for(int i = 1;i <= n;i++){
num2[a[i]]++;
if(num2[a[i]] > 1) break;
if(num[a[i]] == 2) cnt--;
if(cnt == 0){
ll sum1 = (1ll*(1+i)*i)>>1;
ll sum2 = (1ll*(n-i)*(1+n-i))>>1;
if(sum[i] == sum1&&sum[n]-sum[i] == sum2) ans.push_back(i);
}
}
printf("%d\n",ans.size());
for(int i = 0;i < ans.size();i++){
printf("%d %d\n",ans[i],n-ans[i]);
}
}
return 0;
}
C. Dreamoon Likes Coloring
首先,我觉得C题比D题要难想多了
题意: 有n个格子,然后你去涂m种颜色,每种颜色涂一次,第i次涂第i种颜色,且你涂第i次颜色时会涂掉连续的一段长为Li的格子,后面涂的颜色会覆盖掉前面涂的颜色,让你构造出你每次涂颜色的起点使得每个格子都有颜色,且每种颜色都至少出现在一个格子上,如果构造不出来输出-1;
首先一个比较好想出来的特判是,如果Li全部加起来都小于n,显然是不能让每个格子都有颜色的,输出-1;
因为后面涂的颜色会覆盖掉前面的,且要把所有格子涂满,那么先从最后一种颜色开始想,从后往前涂,对于第i种颜色它的最大起点是n-Li+1,最小的起点是i(因为至少要留下i-1个格子给前面涂,否则接下来就会把它全覆盖掉了),若最大起点和最小起点也不符合,那么也是-1;
然后剩下的是可行的,就要构造,额,实在没想到怎么构造~~~~
是看了官方题解:
首先如果可行,有多种构造方法;
我们只取其中一种,就让Pm = n - Lm+1,然后从前往后涂色,Pi+1必须要小于或等于Pi+Li,才能保证不会在中间漏格子没涂色,即有Pi >= Pi+1 - Li
然后继续缩,即有 P1 >= P2 - L2 >= P3 - L1 - L2…>= Pm - L1+L2+Lm-1 = n - L1-L2-…-Lm+1,可以推出来Pi >= n - sum[i] +1 (sum[i]为后缀和,表示Li+Li+1+…+Lm)然后还有前面的一个条件Pi >= i 才能保证第 i 种颜色不被后面的颜色覆盖,在两个约束条件中取个交集;
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n,m;
ll l[N],sum;
int main(){
scanf("%d%d",&n,&m);
bool f = 1;
for(int i = 1;i <= m;i++){
scanf("%lld",l+i);
sum+=l[i];
if(n-l[i]+1 < i) f = 0;
}
if(sum < n) f = 0;
if(!f) { puts("-1");return 0;}
for(ll i = 1;i <= m;i++){
cout<<max(i,n-sum+1)<<" ";
sum-=l[i];
}
return 0;
}
D. Dreamoon Likes Sequences
题意:让你构造一个数列,这个数列至少要有一个元素,它需要满足它是严格递增的,且它的异或前缀和也是严格递增的,可用元素范围为1 ~ d,问你能构造出多少个这样的数列;
首先考虑异或前缀和递增这一点,要做到这个即第一个选了x,那么要接y,那么y至少要比x二进制高一位,为什么呢,因为因为要严格递增所以y至少要大于x,然后如果x和y二进制高位相同,异或起来最高位就是0了,然后无论低位异或结果是什么都不能让异或前缀和递增了,所以至少要二进制高一位,即如果x选了5,那么y至少要用8以上;类似分组背包【1】一组,【2,3】一组,【4,5,6,7】一组,【8,9,10,11,12,13,14,15】一组,同组元素最多选一个,至少选一个元素,用计数DP来做,dp[i]表示以第i组结尾的数列有几个,显然dp[1] = 1,然后以第i组结尾,倒数第二个可以什么都不放,就是cnt[i],cnt[i]就是这组有几个元素,也可以倒数第二个放小于i的组的元素就是dp[j]*cnt[i](j < i), 具体请看代码,很难描述清楚;
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll d,m;
const int N = 40;
int idx;
ll dp[N];//以第i组为结尾的数列的个数
ll cnt[N];//每一组的数量
void init(){
ll l = 1,r;
idx = 0;
while(l <= d){
r = min(d,(l<<(1ll))-1);
cnt[++idx] = r-l+1;
l = r+1;
}
}
inline ll read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int main(){
int t;scanf("%d",&t);
while(t--){
d = read();m = read();
init();
memset(dp,0,sizeof(dp));
dp[1] = 1;
for(int i = 2;i <= idx;i++){
dp[i] = cnt[i];
for(int j = 1;j < i;j++){
dp[i]+=(dp[j]*cnt[i]);
dp[i]%=m;
}
}
ll res = 0;
for(int i = 1;i <= idx;i++){
res+=dp[i];
res%=m;
}
printf("%lld\n",res);
}
return 0;
}