本次考试三道题据出题人的意思都是模板题,T1单调队列模板,T2数位DP模板,T3线段树模板,但由于前两个模板都不熟悉,考试的时候不敢写,线段树也写炸了,最后只有50分。
T1:
题目描述:
在一个包含 n 个元素的数组上,有一个长度为 k 的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到 k 个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而 k 等于 3 :
输入格式
输入的第一行包括两个整数 n,k ,n 表示数组的长度,k 表示窗户的长度。
接下来一行包括 n 个整数,表示这个 n 个元素的数组。
输出格式
输出包含两行,每行包括 n-k+1 个整数。
第一行表示窗户从左到右滑动过程中的最小值。
第二行表示窗户从左到右滑动过程中的最大值。
备注
【数据范围】
对于 100% 的数据,3<=n<=1000000,1<=k<=n,数组中的每个元素均在 int 范围内。
题意:每次选k个数,统计最大值和最小值。所以在我无法打出正解时,想到线段树的区间查询,勉强得了50分,DZY也是线段树但A了,我也没办法。
正解:维护两个单调队列,里面存的是最小值和最大值的下标,一个是维护最小值,一个是维护最大值。每次打新的数压入队尾,先考虑维护最小值的队列,如果前面的数比他还大,说明这些数都无法对答案产生共贡献,就直接r- - (队尾的编号),就将其往前移,最大值也是如此
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
const int N =1e6+5;
int n,k,q1[N],q2[N],a[N],ans1[N],ans2[N],buf[1000];
//---------------------
inline int Readint()
{
int i=0,f=1; char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
//---------------------
inline void W(int x) //这道题因为数据比较大,输出优化
{
if(!x){putchar('0');return;}
if(x<0){putchar('-');x=-x;}
while(x)buf[++buf[0]]=x%10,x/=10;
while(buf[0])putchar('0'+buf[buf[0]--]);
}
//---------------------
int main()
{
freopen("window.in","r",stdin);
//freopen("window.out","w",stdout);
n=Readint(),k=Readint();
for(int i=1;i<=n;i++) a[i]=Readint();
int l1=0,l2=0,r1=1,r2=1;
for(int i=1;i<=n;i++){
while(l1<=r1 && q1[l1]<=i-k) l1++; //最小值的队列
while(l2<=r2 && q2[l2]<=i-k) l2++; //最大值的队列
while(l1<=r1 && a[i]<=a[q1[r1]]) r1--; //比他小就一直减
q1[++r1]=i;
while(l2<=r2 && a[i]>=a[q2[r2]]) r2--; //比他大就一直减
q2[++r2]=i;
ans1[i]=a[q1[l1]];
ans2[i]=a[q2[l2]];
}
for(int i=k;i<=n;i++) W(ans1[i]),putchar(' ');
cout<<endl;
for(int i=k;i<=n;i++) W(ans2[i]),putchar(' ');
return 0;
}
T2: 裸的数位DP
题目描述:
CLC NOIP2015 惨跪,他依稀记得他的准考证号是 37(其实是假的),现在CLC又将要面临一场比赛,他希望准考证号不出现 37(连续),同时他又十分讨厌 4 ,所以也不希望 4 出现在准考证号中。现在他想知道在 A 和 B 之间有多少合法的准考证号
数位Dp 不解释了
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iomanip>
#include<iostream>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
int x,y,a[20],dp[20][2];
//---------------------
inline int Readint()
{
int i=0,f=1; char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
//---------------------
/*
pos是当前枚举到的位数,pre为前一位的数,为了判断是否为3,sta为当前的状态,limit是判断最大能不能枚举到9;
*/
//---------------------
inline int dfs(int pos,int pre,int sta,int limit)
{
if(pos==-1) return 1;
if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
int up= limit ? a[pos] : 9;
int tmp=0;
for(int i=0;i<=up;i++){
if(pre==3 && i==7) continue;
if(i==4) continue;
tmp+=dfs(pos-1,i,i==3,limit && i==a[pos]);
}
if(!limit) dp[pos][sta]=tmp;
return tmp;
}
//---------------------
inline int solve(int x)
{
int pos=0;
while(x) //算出每一位数
{
a[pos++]=x%10;
x=x/10;
}
return dfs(pos-1,-1,0,true);
}
//---------------------
int main()
{
x=Readint(),y=Readint();
memset(dp,-1,sizeof(dp));
cout<<solve(y)-solve(x-1); //统计[0,y]和[0,x-1]的值相减
return 0;
}
T3:
题目描述:
万恶的大头又出现了!他正在玩一个智障游戏:打怪兽。
现在大头的屏幕上出现了一排怪兽,每只怪兽头上有一个血条,每次大头可以选择一个区间进行攻击,攻击值为 K ,这个区间中血量小于 K 的怪兽都会被大头无情地干掉,当然怪兽不会坐以待毙,对于一个区间的怪兽,他们会在某个时刻血量同时加 X 。
头头虽然很大,但是 IQ 并不高,在座的各位选手都不知道比他高到哪里去了。这个时候大头使出了大招——作弊器,然而大头的作弊器并不高级只能将选择的区间内血量为 7 的倍数的怪兽干掉,问:他能干掉多少怪兽?
输入格式
第一行一个正整数 n ;
接下来 n 行 n 个整数;
再接下来一个正整数 Q ,表示操作的个数;
接下来 Q 行每行若干个整数。如果第一个数是 add ,后接 3 个正整数 a,b,X,表示在区间 [a,b] 内每个数增加 X,如果是 count,表示统计区间 [a,b] 能被 7 整除的个数。
正解:线段树打标记,统计每个区间内数值与7取模和 为 0,1,2,3,4,5,6 的个数 ,最后只用暑促余数为0的个数即可
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
const int N = 1e5+5;
int a[N],n,m,x,y,z,num;
int sum[N<<2][10],add[N<<2],tmp[10];
char s[10];
//---------------------
inline int Readint()
{
int i=0,f=1; char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
//---------------------
inline void update(int root)
{
for(int i=0;i<=6;i++)
sum[root][i]=sum[root<<1][i]+sum[root<<1|1][i];
}
//---------------------
inline void mode(int k,int x)
{
memset(tmp,0,sizeof(tmp));
for(int i=0;i<=6;i++) tmp[i]=sum[k][i];
for(int i=0;i<=6;i++) sum[k][(i+x)%7]=tmp[i];
}
//---------------------
void Pushdown(int k)
{
if(add[k])
{
add[k<<1]=(add[k<<1]+add[k])%7,mode(k<<1,add[k]);
add[k<<1|1]=(add[k<<1|1]+add[k])%7,mode(k<<1|1,add[k]);
add[k]=0;
}
}
//---------------------
void build(int k,int l,int r)
{
if(l==r)
{
sum[k][a[l]]++;
return;
}
int mid=l+r>>1;
build(k<<1,l,mid),build(k<<1|1,mid+1,r);
update(k);
}
//---------------------
void modify(int k,int l,int r,int x,int y,int v)
{
if(x<=l&&r<=y)
{
memset(tmp,0,sizeof(tmp));
mode(k,v);
add[k]=(add[k]+v)%7;
return;
}
Pushdown(k);
int mid=l+r>>1;
if(y<=mid)modify(k<<1,l,mid,x,y,v);
else if(x>mid)modify(k<<1|1,mid+1,r,x,y,v);
else modify(k<<1,l,mid,x,mid,v),modify(k<<1|1,mid+1,r,mid+1,y,v);
update(k);
}
//---------------------
inline int Query(int k,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return sum[k][0];
Pushdown(k);
int mid=(l+r)>>1;
if(y<=mid) return Query(k<<1,l,mid,x,y);
else if(x>mid) return Query(k<<1|1,mid+1,r,x,y);
else return Query(k<<1,l,mid,x,y)+Query(k<<1|1,mid+1,r,x,y);
}
//---------------------
int main()
{
freopen("seg.in","r",stdin);
//freopen("seg.out","w",stdout);
n=Readint();
for(int i=1;i<=n;i++) a[i]=Readint()%7;
build(1,1,n);
m=Readint();
for(int i=1;i<=m;i++){
scanf("%s",s);
if(s[0]=='a'){
x=Readint(),y=Readint(),z=Readint()%7;
modify(1,1,n,x,y,z);
}
if(s[0]=='c') {
x=Readint(),y=Readint();
cout<<Query(1,1,n,x,y)<<endl;
}
}
return 0;
}