A 珂朵莉与宇宙
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
星神是来自宇宙的
所以珂朵莉也是吧
所以我就出了个题
给你一个长为n的序列a,有n*(n+1)/2个子区间,问这些子区间里面和为完全平方数的子区间个数
输入描述:
第一行一个数n
第二行n个数表示序列a
输出描述:
输出一个数表示答案
示例1
输入
6
0 1 0 9 1 0
输出
11
备注:
1 <= n <= 100000
0 <= ai <= 10
分析 : 墨迹半天终于墨迹出来 ,TAT 。
第一眼看上去,没有什么好的思路,之后想了好多种方法来解决,各种暴力,DP,貌似都是不行的, 然后看了数据 a[i] 的范围才10,觉得这个应该是个突破口,但是之前想的都没有用到这个关键条件,肯定是不对的,然后再看n的范围,1e5,猜复杂度吧, 应该是o(n*sqrt(n)) . 但是对于sqrt(n)时间复杂度的算法貌似没有几个吧,而且都和这个毫无关系。思路又断了。对于这种区间和问题,肯定要有前缀表 , 写出来数学表达式子,看看有没有什么好的思路吧, ==》 ( sum[ri]-sum[le-1] ) = c * c 。a - b = c * c , a = c * c + b 突然有了感觉,我们可以先枚举b,然后再枚举c,c最大枚举到sqrt( n * a[i] ) , 然后就发现有了sqrt的复杂度, 并且a[i]的大小也用上了,a[i]再大一个0这个方法也就用不上了, 正好和猜测的差不多,应该就是这个思路了。
思路: 有了前缀和之后,我们o(n)的枚举 sum[le-1] , 然后再sqrt的枚举c ,然后ans+= 当前位置之后 a 的个数 。
时间复杂度 o(n * sqrt(n*a[i]))
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAXN = 100000+2;
const int inf = 1e6+11; // n * 10
int sum[MAXN+11]; // 前缀和
int cnt[inf+11]; // 前缀和 的个数。
int ge[inf+11];
int main(){
int n;
while(~scanf("%d",&n)){
int a; sum[0]=0;
memset(cnt,0,sizeof(cnt));
memset(ge,0,sizeof(ge));
cnt[0]++;// 注意0 也要算
for(int i=1;i<=n;i++){
scanf("%d",&a);
sum[i]=sum[i-1]+a; cnt[sum[i]]++;
}
// for(int i=1;i<=n;i++) printf("%d ",sum[i]);puts("");
LL ans=0;
for(int b=0;b<n;b++){
ge[sum[b]]++; // 记录截止到当前位置,有几个sum[b]
for(int c=0;c*c<=inf;c++){
a=sum[b]+c*c ;
if(a>sum[n]) break; // 之后肯定没有意义
ans+=cnt[a]-ge[a]; // 所有的a的个数 - 包括自身之前的a的个数,因为a的位置一定是大于b的位置
}
}
printf("%lld\n",ans);
}
return 0;
}
B 可编程拖拉机比赛
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
“这个比赛,归根结底就是控制一个虚拟的小拖拉机跑完整个赛道。一般一场比赛会有 9 个到 13 个赛道,最后看能跑完多少个赛道。”
通常在一场可编程拖拉机比赛中,分别会有实际参赛队伍数 10%、20%、30% 向下取整的队伍获得金、银、铜牌,其余队伍获得荣誉提名,俗称“铁牌”。
但是主办方往往会多准备一些奖牌,那么在发奖牌的时候会按照比例向上取整发出的奖牌以减少浪费,就会有一些原本获得银牌的队伍获得了金牌。
现在给出一个赛区的规模,也就是这个赛区的实际参赛队伍数,小 Q 同学想知道有多少队伍的奖牌会由银变金、由铜变银、由铁变铜。
输入描述:
输入只有一行,包含一个整数 n (10 <= n <= 1000),表示实际参赛队伍数。
输出描述:
输出一行,包含三个由空格分隔的整数,分别表示奖牌会由银变金、由铜变银、由铁变铜的队伍数。
示例1
输入
115
输出
1 1 2
说明
按照下取整规则只发 11 块金牌的话,第 12 名原本是银牌,但是按照上取整规则发 12 块金牌,第 12 名是金牌,就由银变金了。
分析 :水题 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100 ;
int main(){
int n;
while(cin>>n){
int a=floor(n*0.1); int aa=ceil(n*0.1);
int b=floor(n*0.2); int bb=ceil(n*0.2);
int c=floor(n*0.3); int cc=ceil(n*0.3);
int x=a,xx=aa; // 不同取整前后,金银铜最后一个人的位置
int y=x+b,yy=xx+bb;
int z=y+c,zz=yy+cc;
printf("%d %d %d\n",xx-x,yy-y,zz-z);
}
return 0;
}
C 子序列
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给定一个小写字母字符串T
求有多少长度为m的小写字母字符串S满足,T是S的一个子序列(不需要连续)
输入描述:
第一行一个字符串T
第二行一个正整数m
输出描述:
输出答案对109+7取模的值
示例1
输入
a
2
输出
51
说明
长度为2的里面有a的串有51种
备注:
1<=|T|,m<=105
公式 :
1 从n个不同的物体中可重复的抽取m个不考虑顺序的种类数为 C(n+m-1,m) .
2 从n个不同的物体中可重复的抽取m个进行排列(考虑顺序)的种类数为 n^m == ∑(i=0 - i=m) C(m,i)*(n-1)^(m-i) 。
本题 相当于从 i=T.size() - i=m .
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAXN = 100000+2;
const int inf = 1e6+11;
const int mod = 1e9+7 ;
int fac[MAXN+1];
void init(){
fac[0]=1;
for(LL i=1;i<=MAXN;i++)
fac[i]=fac[i-1]*i%mod;
}
LL qpow(LL a,LL b,LL c){
LL s=1,base=a%c;
while(b){
if(b&1) s=s*base%c;
base=base*base%c;
b>>=1;
}
return s;
}
LL inv(int a){ return qpow(a,mod-2,mod); }
LL C(int n,int m){
return fac[n]*inv((LL)fac[n-m]*fac[m]%mod)%mod;
}
int main(){
init();
string T;int m;
while(cin>>T>>m){
int n=26;
if(m<T.size()) { puts("0"); continue; }
LL ans=0;
for(int i=T.size();i<=m;i++) {
ans+=C(m,i)*qpow(n-1,m-i,mod)%mod;
ans%=mod;
}
cout<<ans<<endl;
}
return 0;
}