头大
这个暑假完就要去搞NOIP了。。。
暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。
课程
鉴于现在就开始模拟noip一考就两天我们吃不消于是变成了隔天考。。八月份回来再实行两天一考。
论T1 RMQ被卡(标答是单调队列) T2数位dp不会 T3线段树不会的尴尬处境。。。
Test 5(0/300)
T1 滑动的窗户
题目描述
在一个包含 n 个元素的数组上,有一个长度为 k 的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到 k 个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而 k 等于 3 :
对于窗户滑动过的每个位置,请给出窗户内 k 个元素的最小值和最大值。
输入格式
输入的第一行包括两个整数 n,k ,n 表示数组的长度,k 表示窗户的长度。
接下来一行包括 n 个整数,表示这个 n 个元素的数组。
输出格式
输出包含两行,每行包括 n-k+1 个整数。
第一行表示窗户从左到右滑动过程中的最小值。
第二行表示窗户从左到右滑动过程中的最大值。
样例数据 1
输入 [复制]
8 3
1 3 -1 -3 5 3 6 7
输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
备注
【数据范围】
对于 100% 的数据,3<=n<=1000000,1<=k<=n,数组中的每个元素均在 int 范围内。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int l,r;
int n,k,num[1000500];
int rmq[1000500][23];
int rmx[1000500][23];
int jud(int a,int b)
{
if(a>=0&&b>=0) return min(a,b);
else if(a>=0&&b<0) return b;
else if(a<0&&b>=0) return a;
else return -max(-a,-b);
}
int check()
{
for(int j=0;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
cout<<"rmx["<<i<<"]["<<j<<"] = "<<rmx[i][j]<<endl;
}
cout << endl;
}
cout << endl;
}
void init()
{
for(int i=1;i<=n;i++)rmq[i][0] = rmx[i][0] = num[i];
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
rmq[i][j] = max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
rmx[i][j] = min(rmx[i][j-1],rmx[i+(1<<(j-1))][j-1]);
}
}
}
int main()
{
freopen("window.in","r",stdin);
freopen("window.out","w",stdout);
cin >> n >> k;
for(int i=1;i<=n;i++)
cin >> num[i];
init();
l=0,r=k-1;
int ky = (int)(log(k)/log(2));
for(int i=1;i<=n-k+1;i++)
{
l+=1; r+=1;
cout << min(rmx[l][ky],rmx[r-(1<<ky)+1][ky]) << " ";
}
l=0,r=k-1; cout << endl;
for(int i=1;i<=n-k+1;i++)
{
l+=1; r+=1;
cout << max(rmq[l][ky],rmq[r-(1<<ky)+1][ky]) << " ";
}
return 0;
}
所以说比赛时还要注意时间限制,空间限制。。贸然用RMQ的后果就是被心狠的出题人摆了一套全部ME。。
标算是单调队列。RMQ(N*logN),单调(N)
STD.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int n,k,head,tail;
int sta[1000050];
int pos[1000050];
inline int read()
{
int data=0,w=1; char ch=0;
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main()
{
n=read(); k=read();
for(int i=1;i<=n;i++)
sta[i]=read();
head = tail = 0;
for(int i=1;i<=n;i++)
{
while(head<tail&&pos[head]<=i-k)++head;
while(head<tail&&sta[pos[tail-1]]>=sta[i])--tail;
pos[tail] = i; tail += 1;
if(i>=k) write(sta[pos[head]]),cout << " ";
}
cout << endl;
head = tail = 0;
for(int i=1;i<=n;i++)
{
while(head<tail&&pos[head]<=i-k)++head;
while(head<tail&&sta[pos[tail-1]]<=sta[i])--tail;
pos[tail] = i; tail += 1;
if(i>=k) write(sta[pos[head]]),cout << " ";
}
}
死的只有那么服气。
T2 准考证号(0/100)
题目描述
CLC NOIP2015 惨跪,他依稀记得他的准考证号是 37(其实是假的),现在CLC又将要面临一场比赛,他希望准考证号不出现 37(连续),同时他又十分讨厌 4 ,所以也不希望 4 出现在准考证号中。现在他想知道在 A 和 B 之间有多少合法的准考证号
输入格式
输入包含两个整数,A B。
输出格式
输出一个整数。
样例数据 1
输入 [复制]
1 10
输出
9
样例数据 2
输入 [复制]
25 50
输出
14
备注
【数据规模和约定】
20% 的数据,满足:1<=A<=B<=1000000 。
100% 的数据,满足:1<=A<=B<=2000000000 。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
long long a,b,a1,b1;
int cnts=0,cntt=0;
int s[15],t[15];
int f[10][10],g4[15];
int get(long long num,int jud)
{
int res=0;
while(num)
{
if(!jud) s[++cnts]=num%10;
else s[++cntt]=num%10;
res+=1; num/=10;
}
return res;
}
void initi()
{
int res = 0,jud = 1;
for(int i=1;i<=10;i++)
{
jud = jud*i;
res = res+jud;
g4[i] = res;
}
}
int get4(int num,int cnt)
{
if(num<4) cnt-=1;
return g4[cnt];
}
int g37[10] = {0,0,1,2,4,7,12,20,32,50};
int get37(int num,int cnt)
{
if(num<4) cnt-=1;
return g37[cnt];
}
int main()
{
freopen("ticket.in","r",stdin);
freopen("ticket.out","w",stdout);
cin >> a >> b;
initi();
a1 = get(a,0); b1 = get(b,1);
int ans1=0,ans2=0;
for(int i=b1;i>=1;i--)
{
ans1 += get4(s[i],i) + get37(s[i],i);
}
for(int i=a1;i>=1;i--) ans2 += get4(s[i],i) + get37(s[i],i);
cout << ans1-ans2 << endl;
return 0;
}
一通乱搞想搞出数位dp。。。但这乱搞得八竿子都打不着也是可以。。
答案就是数位dp,而且几乎是裸题。(不要62)
STD.CPP
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
long long n,m;
int a[10],dp[10][3];
long long calc(long long x)
{
long long sum = x;
long long ans = 0;
int num = 0;
bool flag = false;
while(x)
{
a[++num] = x%10;
x/=10;
}
a[num+1] = 0;
for(int i=num;i>=1;i--)
{
ans += dp[i-1][2]*a[i];
if(flag) ans += dp[i-1][0]*a[i];
else
{
if(a[i]>4) ans += dp[i-1][0];
if(a[i+1]==3&&a[i]>7) ans += dp[i][1];
if(a[i]>3) ans += dp[i-1][1];
if(a[i]==4||(a[i+1]==3&&a[i]==7)) flag = true;
}
}
if(flag) ans++;
return sum-ans;
}
void initi()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=9;i++)
{
dp[i][0] = dp[i-1][0]*9 - dp[i-1][1];
dp[i][1] = dp[i-1][0];
dp[i][2] = dp[i-1][0] + dp[i-1][1] + dp[i-1][2]*10;
}
}
int main()
{
initi();
cin >> n >> m;
cout << calc(m) - calc(n-1) << endl;
}
T3 Query(0/100)
题目描述
万恶的大头又出现了!他正在玩一个智障游戏:打怪兽。
现在大头的屏幕上出现了一排怪兽,每只怪兽头上有一个血条,每次大头可以选择一个区间进行攻击,攻击值为 K ,这个区间中血量小于 K 的怪兽都会被大头无情地干掉,当然怪兽不会坐以待毙,对于一个区间的怪兽,他们会在某个时刻血量同时加 X 。
大头头虽然很大,但是 IQ 并不高,在座的各位选手都不知道比他高到哪里去了。这个时候大头使出了大招——作弊器,然而大头的作弊器并不高级只能将选择的区间内血量为 7 的倍数的怪兽干掉,问:他能干掉多少怪兽?
输入格式
第一行一个正整数 n ;
接下来 n 行 n 个整数;
再接下来一个正整数 Q ,表示操作的个数;
接下来 Q 行每行若干个整数。如果第一个数是 add ,后接 3 个正整数 a,b,X,表示在区间 [a,b] 内每个数增加 X,如果是 count,表示统计区间 [a,b] 能被 7 整除的个数。
输出格式
对于每个询问输出一行一个答案。
样例数据 1
输入 [复制]
3
2 3 4
6
count 1 3
count 1 2
add 1 3 2
count 1 3
add 1 3 3
count 1 3
输出
0
0
0
1
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n,q,cnt,mos[100050],pie[320][10],pos[20];
int a,b,x;
char c[10];
void add()
{
cin >> a >> b >> x;
int l = a/cnt+1;
int r = b/cnt-1;
if(l<=r)
{
for(int i=a;i<=l*cnt;i++)
{
pie[l-1][mos[i]]-=1;
mos[i] = (mos[i]+x)%7;
pie[l-1][mos[i]]+=1;
}
for(int i=l+1;i<=r;i++)
{
for(int j=0;j<=6;j++)
pos[j+x] = pie[i][j];
for(int j=0;j<=6;j++)
pie[i][(j+x)%7] = pos[j+x];
for(int j=1;j<=cnt;j++)
mos[i*cnt+j] = (mos[i*cnt+j]+x)%7;
}
for(int i=(r+1)*cnt+1;i<=b;i++)
{
pie[r][mos[i]]-=1;
mos[i] = (mos[i]+x)%7;
pie[r][mos[i]]+=1;
}
}
else if(l>r)
{
if(l==r+1)
{
for(int i=a;i<=l*cnt;i++)
{
pie[l-1][mos[i]]-=1;
mos[i] = (mos[i]+x)%7;
pie[l-1][mos[i]]+=1;
}
for(int i=(r+1)*cnt+1;i<=b;i++)
{
pie[r][mos[i]]-=1;
mos[i] = (mos[i]+x)%7;
pie[r][mos[i]]+=1;
}
}
else
{
for(int i=a;i<=b;i++)
{
pie[r+1][mos[i]]-=1;
mos[i] = (mos[i]+x)%7;
pie[r+1][mos[i]]+=1;
}
}
}
}
void count()
{
int ans = 0;
cin >> a >> b ;
int l = a/cnt+1;
int r = b/cnt-1;
if(l<=r)
{
for(int i=a;i<=l*cnt;i++)
if(mos[i]==0) ans += 1;
for(int i=l;i<=r;i++)
ans += pie[i][0];
for(int i=r*cnt+1;i<=b;i++)
if(mos[i]==0) ans += 1;
}
else if(l>r)
{
if(l==r+1)
{
for(int i=a;i<=l*cnt;i++)
if(mos[i]==0) ans += 1;
for(int i=(r+1)*cnt+1;i<=b;i++)
if(mos[i]==0) ans += 1;
}
else
{
for(int i=a;i<=b;i++)
if(mos[i]==0) ans += 1;
}
}
cout << ans << endl;
}
int main()
{
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
cin >> n;
cnt = int(sqrt(n));
for(int i=1;i<=n;i++)
{
cin >> mos[i];
mos[i] = mos[i]%7;
pie[i/cnt+1][mos[i]]+=1;
}
cin >> q;
while(q--)
{
cin >> c;
if(c[0]=='a') add();
else count();
}
return 0;
}
考试时看到区间就想到了之前讲过的分块(小伙子有想法),但是只有理论又没有打过代码那只有GG一条路可走。。。分块事后证明只会有几个点TE。
标答是线段树。。而且是和模板出入很大的那种。
STD.CPP
//std ans of day* t3 线段树
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstdio>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
const int Maxn=1e5+50;
char ch[10];
int a[Maxn],sum[Maxn<<2][8],tag[Maxn<<2];
int S[8];
inline int read()
{
char ch=getchar();
int i=0,f=1;
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
i=(i<<1)+(i<<3)+ch-'0';
ch=getchar();
}
return i*f;
}
inline void update(int now)
{
for(int i=0;i<7;++i)
sum[now][i]=sum[now<<1][i]+sum[now<<1|1][i];
}
inline 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);
}
inline void addtag(int k,int v)
{
tag[k]=(tag[k]+v)%7;
for(int i=0;i<7;++i)
{
int t=(i+v)%7;
S[t]=sum[k][i];
}
for(int i=0;i<7;++i)
sum[k][i]=S[i];
}
inline void pushdown(int k)
{
addtag(k<<1,tag[k]);
addtag(k<<1|1,tag[k]);
tag[k]=0;
}
inline int query(int k,int l,int r,int L,int R)
{
if(l>=L&&r<=R)
return sum[k][0];
if(tag[k])
pushdown(k);
int mid=(l+r)>>1;
if(R<=mid)
return query(k<<1,l,mid,L,R);
else
if(L>mid)
return query(k<<1|1,mid+1,r,L,R);
else
return query(k<<1,l,mid,L,R)+query(k<<1|1,mid+1,r,L,R);
}
inline void modify(int k,int l,int r,int L,int R,int v)
{
if(l>=L&&r<=R)
{
addtag(k,v);
return;
}
if(tag[k])
pushdown(k);
int mid=(l+r)>>1;
if(R<=mid)
modify(k<<1,l,mid,L,R,v);
else
if(L>mid)
modify(k<<1|1,mid+1,r,L,R,v);
else
{
modify(k<<1,l,mid,L,R,v);
modify(k<<1|1,mid+1,r,L,R,v);
}
update(k);
}
int main()
{
int n=read();
for(int i=1;i<=n;++i)
a[i]=read()%7;
build(1,1,n);
int Q=read();
while(Q--)
{
scanf("%s",ch+1);
int x=read(),y=read();
if(ch[1]=='c')
cout<<query(1,1,n,x,y)<<endl;
else
{
int v=read()%7;
modify(1,1,n,x,y,v);
}
}
return 0;
}
。。当成是吃一堑长一智吧。。这周搞搞数位DP。。线段树额。。搞搞吧。。
我还想搞KMP和AC自动机来着。。orz