目录
A - Jessica's Reading Problem
题意:
杰西卡·黄面临期末考试的严峻挑战,她平时并没有认真听课,但一个追求她的男生帮她画了范围和知识点,因此两人的关系更进一步。
现在书上一共有P页,每页上有一个关于a[i]的知识点,知识点可能重复。
请问杰西卡最少需要看几页就能复习完。
思路:
先用想办法计算出总共有多少种知识点。
之后便是尺取找最短的序列满足条件。
-
考察点:尺取,思维
代码:
//卡输入,必须scanf或快读
//关闭输入流+"\n"还是会T
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
map<int,int> mp;
int a[maxn];
int main()
{
int n,k=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(mp[a[i]]==0) k++;
mp[a[i]]=1;
}
mp.clear();
int l=1,r=1;
int cnt=0,len=inf;
while(l<=n){
while(r<=n&&cnt<k){
if(mp[a[r]]==0) cnt++;
mp[a[r++]]++;
}
if(cnt<k) break;
len=min(len,r-l);
mp[a[l]]--;
if(mp[a[l++]]==0) cnt--;
}
printf("%d\n",len);
}
B - Bound Found
题意:
现在给你一个长度为n的序列a,a[i]<=10000。
接下来会有k次询问,每次询问会给出一个值t。
找到a的一个子序列使得这个子序列的和的绝对值尽可能地接近t。
输出这个子序列的序列和它的起始位置与终止位置。
思路:
首先求出1~r的所有的前缀和并进行存储。
接下来进行尺取,具体过程请看代码注释。
-
考察点:尺取,排序,思维
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+500;
struct node
{
int x,r; ///x:前缀和 r代表:1~r之前的数的和为x
node(){}
node(int xx,int rr):x(xx),r(rr){}
};
bool cmp(node z,node c){
return z.x<c.x;
}
node a[maxn];
int main()
{
int n,k,t;
ios::sync_with_stdio(false);
while(cin>>n>>k&&n&&k){
a[0].x=0;a[0].r=0;
for(int i=1;i<=n;i++){
cin>>a[i].x;
a[i].x=a[i].x+a[i-1].x;
a[i].r=i;
}
sort(a,a+1+n,cmp);
// for(int i=1;i<=n;i++){
// cout<<a[i].x<<" "<<a[i].r<<endl;
// }
for(int i=0;i<k;i++){
cin>>t;
int l=0,r=1;
int finl,finr;
int mint=inf;
int ans;
while(mint&&r<=n){ //mint=0 =>存在完全相符的答案,直接输出即可
int chazhi=a[r].x-a[l].x;
//cout<<a[r].r<<" "<<a[l].r<<" "<<chazhi<<" "<<mint<<endl;
if(abs(chazhi-t)<=mint){
ans=chazhi;
finl=a[l].r;
finr=a[r].r;
mint=abs(chazhi-t);
}
if(chazhi>t) l++;
if(chazhi<t) r++;
if(l==r) r++;
}
if(finl>finr) swap(finl,finr);
cout<<ans<<" "<<finl+1<<" "<<finr<<endl;
}
}
}
C - Subsequence
题意:
给定一个长为n的序列a,求a的一个最短子序列长度满足:
该子序列和>=S
思路:
裸的尺取,干就完了!
-
考察点:尺取
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+500;
int a[maxn];
int main()
{
int t,n,s;
ios::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n>>s;
for(int i=1;i<=n;i++)
cin>>a[i];
int l=1,r=1;
int ans=inf;
int sum=0;
while(l<=n){
while(sum<s&&r<=n){
sum+=a[r++];
}
if(sum<s)
break;
ans=min(ans,r-l);
sum-=a[l++];
}
if(ans==inf) cout<<"0\n";
else cout<<ans<<"\n";
}
}
D - Tallest Cow
题意:
FJ有N头牛,先给出最高的牛的编号和高度。(牛的高度为整数)
接下来会给出r组数据:
x y
代表x牛平视y牛,编号(x+1)~(y-1)的牛会比两边矮(矮多少不知道,反正就是矮)
请你最后给出每头牛最高是多少?
思路:
利用到差分数组的特性:处理区间增减问题,非常的简单。
-
考察点:差分数组
-
坑点:会有重复数据出现
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=1e4+500;
int a[maxn];
int main()
{
int n,i,h,r,x,y;
map<pair<int,int>,int> mp;
cin>>n>>i>>h>>r;
memset(a,0,sizeof(a));
a[0]=h;
for(int i=0; i<r; i++)
{
cin>>x>>y;
if(x>y) swap(x,y);
if(mp[make_pair(x,y)])
continue;
mp[make_pair(x,y)]=1;
a[x+1]-=1;
a[y]+=1;
// for(int i=1; i<=n; i++)
// {
// if(i!=1)
// cout<<" ";
// cout<<a[i];
// }
// cout<<endl;
}
for(int i=1; i<=n; i++)
{
a[i]=a[i]+a[i-1];
}
for(int i=1; i<=n; i++)
{
if(i!=1)
cout<<" ";
cout<<a[i];
}
cout<<endl;
}
E - Straight Master
题意:
给你一个长为n的数组,每次你可以选择一个长为3,4,5的区间使其中的值减1。
问是否能把所有的数变成0。
思路:
利用差分数组特性:
对于数组a产生的差分数组b,使数组a的a[l]~a[r]全部减1=>b[l]-1 b[r+1]+1
接下来是关键:
- b[1]==a[1],所以b[1]一定为正数。
- b[2],b[3]若小于0,对于有效范围内没有数据可以填补两者。(b[2]想变成正数就需要b[2-1-3]减一,但不存在,b[3]同理)
- 最后在b[len]后添加一个b[len+1]==0-a[len],用于减去最后一个元素a[len]。
接下来就很简单了,就是拿前面的正数去弥补后面的负数。如果正好为0则说明可以,否则不可以。
其中还要注意边界判断。
-
考察点:数学,思维,差分
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
ll a[maxn],b[maxn];
int main()
{
ios::sync_with_stdio(false);
int t,n;
cin>>t;
for(int c=1;c<=t;c++){
cin>>n;
a[0]=0;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
b[i]=a[i]-a[i-1];
b[n+1]=0-a[n];
if(b[2]<0||b[3]<0){
cout<<"Case #"<<c<<": No\n";
continue;
}
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=b[i];
int pos=i+3;
if(pos>n+1) //边界
break;
if(b[pos]<0){
sum+=b[pos];
b[pos]=0;
}
if(sum<0) break;
}
if(sum==0) cout<<"Case #"<<c<<": Yes\n";
else cout<<"Case #"<<c<<": No\n";
}
}
F - 非常男女
题意:
中文你我?懂?
思路:
01可能不是很方便我们统计人数,将代表女生的0换成-1,那么这道题就转换成求一个最长子序列满足序列和为0。
-
考察点:前缀和,思维
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=1e5+500;
int a[maxn];
int sum[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==0) a[i]=-1;
}
sum[0]=0;
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
map<int,int> mp;
int len=-1;
for(int i=1;i<=n;i++){
if(sum[i]==0){
len=max(len,i);
}
else{
if(mp[sum[i]]&&len<i-mp[sum[i]])
len=i-mp[sum[i]];
else if(!mp[sum[i]])
mp[sum[i]]=i;
}
}
cout<<len<<endl;
}
G - 矩形A + B
题意:
中文你我?懂?
思路:
排列组合问题,共有(n+1)条横着的线和(m+1)条竖着的线,问可以产生多少个矩形?
-
考察点:数学,思维,排列组合
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
cout<<((n+1)*n/2)*((m+1)*m/2)<<endl; //C[2](n+1) * C2(m+1)
}
}