我真的.真的..真的...炒鸡炒鸡高兴的说!
因为,在noip前16天,我快要退役的时候,居然 ...
AK辣 ~\(≧▽≦)/~ 啦啦啦
好吧我承认我是 295分 ......
而且这么高是因为第二题抄了 斜率优化 的模板,
第三题和 LRZ大神 讨论了并且抄了他的dfs ......
可能是这套题比较符合我的口味吧?
而且很快的切掉了第一题...特别顺利的说!
反正无论如何,就是很高兴啊、很高兴啊 ovo
感谢 wuli 大男神 && ZJ && Rin && 各大男神 qwq
目录
【T1】听写(dictation)--- 英语题 + 找规律
【T2】遥远的金字塔(pyramid)--- 数学题 + 斜率优化 DP
【T3】心灵治愈(heal) --- 语文题 + 数论 + DFS
【T1】听写(dictation)
- 老师给大家一种全新的语言,要你来鉴别它们是不是合法的。
1.一个句子由主语、谓语和宾语按先后顺序排列而成,
缺少其中任何一个成分都不是这种语言;
2.语言中存在两种从句:主语从句和定语从句;
3.对于主语从句,从句里可以有新的从句,
从句只可替代应有的部分,所以并不会出现形式主语等;
4.对于有定语从句的句子,定语从句只能修饰宾语(放在宾语后) ,
而且其修饰的宾语应该省略,定语从句里也可以有新的从句。
- 为了简化问题,用 z 代表主语,w 代表谓语,b 代表宾语。
【输入格式】
第一行是数据组数 T。接下来 T 行表示 T 段听写的内容,
注意一段听写内容中可能有多句话, 但句与句之间没有标点。
【输出格式】
对于每组数据,输出一或两行。如果当前这段听写内容出现了问题,
那么输出“Yes”,否则输出“No” ,并在接下来的一行里输出这段话中从句的总个数。
【样例输入】
- 3
- zwz
- zwbzw
- zwbwb
【样例输出】
- Yes
- Yes
- No
- 1
【标签】找规律 + 分类讨论 + 模拟
貌似是独创方法???好像dalao和官方题解都不是这样写的来着...
难道是我终于聪明了一回???哈哈哈......qwq
方法就是,找规律,发现原串可以分成:
- zwb wb w zwb w zwb w zwb w zwb w zw zwb wb wb wb wb w zwb
- w zwb wb wb w zwb wb w zwb w zw zwb w zwb wb w zw zw zw zw zw
- zw zwb wb w zwb wb wb w zw zwb (答案为 49)
为什么这样分?断开所有的 ‘ w ’。并且是按照规律断开。
【规律】如果是合法序列,‘ w ’ 的存在形态只可能是:
- w ; wb ; zw ; zwb 。
然后对应的分别构成 2, 1, 1, 0 个从句(按省略的z或b计算)。
这样只要特判一下不可能的情况,顺序扫一遍就行了....qwq。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
//【听写】
//zwb wb w zwb w zwb w zwb w zwb w zw zwb wb wb wb wb w zwb
//w zwb wb wb w zwb wb w zwb w zw zwb w zwb wb w zw zw zw zw zw
//zw zwb wb w zwb wb wb w zw zwb
void reads(ll &x){ //读入优化(正负整数)
ll fx=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=fx; //正负号
}
char ss[100019];
int main(){
freopen("dictation.in","r",stdin);
freopen("dictation.out","w",stdout);
int n; scanf("%d",&n);
for(int kase=1;kase<=n;kase++){
scanf("%s",ss); int i=0,len=strlen(ss),ans=0,okk=true;
if(ss[0]!='z'||ss[len-1]!='b')
{ printf("Yes\n"); continue; }
while(i<len){
if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='b')
{ i+=3; continue; }
if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='w')
{ printf("Yes\n"); okk=false; break; }
if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='z')
{ i+=2; ans++; continue; } //填一个b
if(ss[i]=='z') //z后面不是w,或者 zw 在末尾,都是不行的
{ printf("Yes\n"); okk=false; break; }
if(ss[i]=='w'&&ss[i+1]=='w') //两个w不能相连
{ printf("Yes\n"); okk=false; break; }
if(ss[i]=='w'&&ss[i+1]=='b') //wb + z/w..
{ i+=2; ans++; continue; }
if(ss[i]=='w'&&ss[i+1]=='z') //w + z..
{ i++; ans+=2; continue; }
if(ss[i]=='w') //单独的 w 在末尾,是不行的
{ printf("Yes\n"); okk=false; break; }
if(ss[i]=='b') //不可能存在单独的b
{ printf("Yes\n"); okk=false; break; }
} if(okk==true) cout<<"No"<<endl<<ans<<endl;
}
}
【T2】遥远的金字塔(pyramid)
- 在历史课上,你有一个金字塔的侧视图。这个金字塔一共有 n 层,
- 从下至上编号为 1至 n。除了第一层外,其余层均被它底下的一层包含。
- 即如果 xi,yi分别表示第 i 层的左、右端点,则对于任意的 i≥2,都有 xi≥xi-1,且 yi≤yi-1。
- 每一层矩形的高度均为 1。 在整个侧视图中选择恰好 k 个不相交的矩形 ,
- 问这 k 个不相交的矩形覆盖的面积最大是多少。
【输入格式】
第一行两个整数 n 和 k。第二至 n+1 行,
其中第 i+1 行两个整数 xi和 yi,分别表示第 i 层金字塔的左右端点。
【输出格式】
只包含一个整数,即最大覆盖面积。
【样例输入】
- 5 3
- 1 6
- 1 5
- 3 5
- 4 4
- 4 4
【样例输出】
- 15
【标签】DP + 斜率优化 + 单调队列维护单调性
......普通 DP 80分,就是这个式子:
- f [ i ][ j ] = max( f [ k ][ j - 1 ] + ( y[ i ] - x [ i ] ) * ( i - ( k + 1 ) + 1 ) ) ;
然后我写斜率优化坑了好久...结果只加上了15分...
好吧我确实是傻逼...要化除为乘才能AC2333...
具体分析过程看代码吧 ovo 注释超详细的 qwq
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
//【遥远的金字塔】
//f[i][j]表示已经到第i层(从下到上),已经选了j个矩形的max面积和。
//枚举包含i的矩形的最下层(编号小)的下一层k=( j-1 ~ i-1 )
//为什么从j-1开始?因为如果一层选两个矩形,一定不是最优。
//f[i][j]=max(f[k][j-1]+(y[i]-x[i])*(i-(k+1)+1));
//因为金字塔满足包含关系,所以最短的一定是最上层的y[i]-x[i]。
//枚举k肯定会超时啦啦啦...所以尝试一下单调队列优化?
//f[i][j]=max(f[k][j-1]+a[i]*i-a[i]*k);
//f[i][j]=max(f[k][j-1]-a[i]*k) + a[i]*i;
//暂时把j这一维省略掉...f[i]=max(f[k]-a[i]*k)+a[i]*i;
//这东西好眼熟...有点像...斜率优化?
//yi-xi*k>=yk-xk*k (k=a[i])
//a[i]<=f[i]-f[k]/(i-k);我真的忘了斜率优化怎么做的了...
//slop(i,k)=f[i]-f[k]/(i-k)?然后用单调队列...?
//维护队头的可行性(对于每个新的i),队尾的单调性...队头即为最优解。
void reads(ll &x){ //读入优化(正负整数)
ll fx=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=fx; //正负号
}
ll f[20019][109],q[109][20019],a[20019],head[109],tail[109];
double slop(ll k1,ll k2,ll j){ return (double)(f[k2][j]-f[k1][j]); }
int main(){
freopen("road.in","r",stdin);
freopen("pyramid.out","w",stdout);
ll n,m,x,y,ans=0; reads(n),reads(m);
for(ll i=1;i<=n;i++) reads(x),reads(y),a[i]=y-x+1;
for(ll j=0;j<=m;j++) head[j]=1,tail[j]=1,q[j][1]=0;
for(ll i=1;i<=n;i++)
for(ll j=min(i-1,m-1);j>=0;j--){ //↓↓维护队头可行性
while(head[j]<tail[j]
&&slop(q[j][head[j]],q[j][head[j]+1],j)
>(double)a[i]*(q[j][head[j]+1]-q[j][head[j]])) head[j]++;
f[i][j+1]=f[q[j][head[j]]][j]-a[i]*q[j][head[j]]+a[i]*i;
//(q[j][head[j]]+1)~i行组成矩形,答案最优
while(head[j+1]<tail[j+1]
&&slop(q[j+1][tail[j+1]-1],q[j+1][tail[j+1]],j+1)*(i-q[j+1][tail[j+1]])
<slop(q[j+1][tail[j+1]],i,j+1)
*(q[j+1][tail[j+1]]-q[j+1][tail[j+1]-1])) tail[j+1]--;
q[j+1][++tail[j+1]]=i; //↑↑↑需要将i入队,但要保持队列斜率单调性
}
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
if(ans<f[i][j]) ans=max(ans,f[i][j]);
cout<<ans<<endl; return 0;
}
【T3】心灵治愈(heal)
- “假设这里有一条长轴,在 0 号位置有一个小球,它代表一个人来临到这个世界,
- 开始了他的人生道路。他将很多个人生境况,每个境况对人生可能产生多次影响。
- 而对于每一次影响,它也许能帮助你前进,但如果你不能保持成功不骄傲,
- 失败不气馁,那表面看起来的好的境况也许会让你后退,因此一个人有可能会走到负数。
- “而大部分境况的影响值是由你后天所决定的。如果你能好好认识与对待境况,
- 它们就会让你前进相应的影响值。否则,你可能会后退相应的影响值,或者待在原点。
- “但是人类无法摆脱贪婪的特点,他们始终尽可能的前进。 但是,大家似乎都忽略了一点,
- 那就是人生的终点其实就在 1 号点。许许多多的人由于境况影响值的原因,无法到达生命的
- 终点,那他的一生就是不圆满的。回到原点的前面,这大概就是生命的真谛吧。 ”羽点点头。
- “在生命终点的境况的影响永远是最大的,它之前没有哪一个境况的影响值是大于它的。
- 因为在生命终点浮现在你脑海里的永远是对你最重要的东西。
- 并且生命结束的方式和其影响值其实在你出生的时候就已经决定了。
- “那么从人生的起点到终点,成长羁绊的境况的影响值组合究竟有多少种情况呢?”
【输入格式】
输入文件有且仅有一行,包括用空格分开的两个整数 N 和 M。
N 表示一共有 N+1 个人生境况,M 表示生命终点的境况的影响值。
【输出格式】
输出有多少种境况的影响值的组合能满足可以从人生起点走到终点。
由于答案可能很大,输出它模 1000000007的值。注意,境况是按时间有序的。
【样例输入1】
- 2 3
【样例输出1】
- 8
【样例输入2】
- 8 8
【样例输出 2】
- 16711680
【样例说明】
为了不影响【题目描述】的优美性,题意的进一步解释将放在【样例说明】中。
1.境况的总个数已经确定为 N+1,除了第 N+1 个境况,其他境况的影响值未知;
2.长轴表示人生的道路,每一个点对应人生的一个位置,长轴无限长,
但长轴与境况没有关系,即任一境况可以在任意的位置发生,位置对境况没有任何影响;
3.生命结束的方式,即第 N+1 个境况,它的影响值已经在出生时就给定为 M;
4.其他境况(第 1-N 个境况)的影响值不会大于第 N+1 个境况的影响值,
且其影响值一定是正的, 但在长轴上的效果是可以是负的,
后退即在当前位置减去这个境况的影响值到达新的位置;
5.每一个境况都会影响人生至少一次,同一个境况可以影响人生多次,
并且每一次对人生的影响效果(前进 或 后退 或 停在原地)可以不同;
6.长轴的点不一定都会经过,每一个境况会依次经过;
7.题目要求的是有多少种境况影响值的组合,使得在经历了这些境况之后,
可以从生命起点到达生命的终点,境况具体怎么影响人生可参见下面的解释;
8.境况的影响值组合是一个长度为 N+1 的序列,第 i 个数代表第 i 个境况的影响值
(不需要输出) ,注意:境况的影响值并不是该境况对人生产生影响的次数,
而是每一次该境况对人生产生影响效果时前进或后退的距离 。
9.境况是按时间有序的,即同样的组合,不同的顺序也分别计入答案。
对于样例 1,这 8 种可能的 境况影响值组合 分别是:
(1, 1, 3), (1, 2, 3), (1, 3, 3), (2, 1, 3), (2, 2, 3), (2, 3, 3), (3, 1, 3), (3, 2, 3).
【标签】题面理解 + 大胆猜想 + 质因数 +容斥原理
题意转化为:a[n+1]=m,a[1~n]<m,求所有的数的最大公因数是1的方案数。
...一开始算错了4^8...然后发现 8 8 真的就是 8^8-4^8...
(1)总数m^n。分解m=p1^k1*p2^k2*...*pt^kt,
那么选择的数不能全部都是pi的倍数:- (m/pi)^n。
但重复减掉了完全是pi*pj的倍数的情况(一共两个质因子)。
(2)如果有两个因子相乘的倍数pi*pj,+ (m/(pi*pj))^n。
(3)如果有三个因子相乘的倍数pi*pj*pk,
先被减了3次,又被两两组合,加了3*(3-1)/2=3次。
那么还要 - (m/(pi*pj*pk))^n。
(4)如果有四个因子相乘的倍数pi*pj*pk*pl,
先被减了4次,又被两两组合加了4*(4-1)/2=6次,
还被三三组合减了4次,那么还要再加一次 + (m/(pi*pj*pk*pl))^n。
(5)如果有五个因子相乘的倍数pi*pj*pk*pl*pt,
先被减了5次,又被两两组合加了5*(5-1)/2=10次,
被三三组合减了10次,被四四组合加了5次,
还要减一次 - (m/(pi*pj*pk*pl*pt))^n。
然后就发现了规律...不过好像容斥原理里面本来就有这种定理吧??
质因子数相乘<10^15,所以质因子个数不会超过15个...
dfs就行...(我才不要说这个dfs是抄的 LRZ大神 的 qaq)
大概我能看懂题意,是因为我语文比较好吧???
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
//【心灵治愈】
//a[n+1]=m,a[1~n]<m,求所有的数的最大公因数是1的方案数
//...一开始算错了4^8...然后发现 8 8 真的就是 8^8-4^8...
//总数m^n。分解m=p1^k1*p2^k2*...*pt^kt,
//那么这些数不能完全是pi的倍数:-(m/pi)^n。
//但重复减掉了完全是p1*p2的倍数的情况(一共两个质因子)。
//如果有两个因子相乘的倍数pi*pj,+(m/(pi*pj))^n。
//如果有三个因子相乘的倍数pi*pj*pk,
//先被减了3次,又被两两组合,加了3*(3-1)/2=3次。
//那么还要 -(m/(pi*pj*pk))^n。
//如果有四个因子相乘的倍数pi*pj*pk*pl,
//先被减了4次,又被两两组合加了4*(4-1)/2=6次,
//还被三三组合减了4次,那么还要再加一次 +(m/(pi*pj*pk*pl))^n。
//如果有五个因子相乘的倍数pi*pj*pk*pl*pt,
//先被减了5次,又被两两组合加了5*(5-1)/2=10次,
//被三三组合减了10次,被四四组合加了5次,
//还要减一次 -(m/(pi*pj*pk*pl*pt))^n。
//然后就发现了规律...质因子数相乘<10^15,应该不会超过20个吧...
void reads(ll &x){ //读入优化(正负整数)
ll fx=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=fx; //正负号
}
const ll mod=1000000007;
ll primes[50019],cnt=0,n,m,ans; //质因子
void get_prime(ll x){ //找到x的所有质因子
for(int i=2;i<=sqrt(x+1);i++){
if(x%i!=0) continue;
primes[++cnt]=i; //记录质因子
while(x%i==0) x/=i; //除掉所有i
} if(x!=1) primes[++cnt]=x;
}
ll power(ll a,ll b){ //ksm
ll anss=1; a=a%mod;
while(b>0){
if(b&1) anss=(anss*a)%mod;
a=a*a%mod; b>>=1;
} return anss;
}
void dfs(ll flag,ll now_cnt,ll now_sum){
if(now_cnt==cnt+1){ //递归的最底层
if(flag&1) ans=(ans-power(m/now_sum,n)+mod)%mod;
else ans=(ans+power(m/now_sum,n))%mod;
return; //判断2^cnt种选择方案之一的情况、加或减的对答案的影响
} dfs(flag^1,now_cnt+1,now_sum*primes[now_cnt]);
dfs(flag,now_cnt+1,now_sum); //每个因子可以选择乘上或者不乘
}
int main(){
freopen("heal.in","r",stdin);
freopen("heal.out","w",stdout);
reads(n),reads(m),get_prime(m);
dfs(0,1,1); //flag,当前枚举到的因子数,当前凑成的因子积
//flag中的0表示此层为奇数层,需要减,1表示此层为偶数层,需要加
cout<<ans<<endl; return 0;
}
好了题目都分析完了...再次怀念一下AK的故事qwq
——时间划过风的轨迹,那个少年,还在等你。