萌新又来写题解啦
原题链接
B 减成一
题意:存在n个数,每次操作可以任选一个区间使得区间内的所有数字减一。问最少多少次操作,可以让所有数都变成1。
tips:洛谷原题“积木大赛”。类似的想法,连数组都不需要,只要一个last变量记录“上一个数据操作了几次”。注意如果输入的数据是1,last要置为0,表示“上一个数据不需要操作”。
答案是用差分数组做的,明显更麻烦一点~
但是可以借鉴思路!
“通过计算出差分数组,操作可以转为先选 取一个数减一再选取一个数加一(区间左端点-1,右端点后一个+1)。目标的差分数组也就变成了第一个数为1,其余为0的数组(1,1,1的差分为1,0,0)。
• 最快操作方式就是将差分数组第一个数减为1,其余减为0。即操作数为差分数组的正数之和-1。”
#include
#define ll long long
using namespace std;
int t,n;
ll num,ans,last,tmp;
void solve(){
scanf("%d",&n);
ans = last = 0;
for(int i = 1; i <= n; ++i){
scanf("%lld",&num);
if(num == 1){
last = 0;
continue;
}
tmp = num - 1;
if(tmp > last)
ans += tmp - last;
last = tmp;
}
printf("%lld\n",ans);
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
C 面积
tips:签到题。注意PI取3.14.直接算面积即可。
#include
using namespace std;
const double PI = 3.14;
int t;
double x,ans;
int main(){
scanf("%d",&t);
while(t--){
scanf("%lf",&x);
ans = x * x + x * x * PI / 2.0;
printf("%.2lf\n",ans);
}
return 0;
}
D 扔硬币
题意:扔下了n枚硬币后,已知至少有m枚硬币是反面。请问恰好有k枚硬币是正面的概率是多少。
tips:条件概率。分两种情况:
A.对于k+m>n是不可能的,直接输出0.
B.其余情况:C(n,k)/[ 2^n - ∑(C(n,i)) ] (i
套用条件概率公式。A事件为“扔n枚硬币,恰好有k枚硬币正面向上”(分子)。B事件为“扔n枚硬币,至少有m枚硬币反面向上。将至少转化为至多,即有0,1,2……(m-1)枚硬币反向的概率,用1减去即为所求。
处理完表达式后,直接用逆元+费马小定理处理数据即可。注意n 1e5的数据量,求阶乘的时候不要用(a * b ) % mod,而要使用(a % mod) * (b % mod) % mod防止溢出。
#include
#define ll long long
const ll mod = 1e9 + 7;
int n,m,k,t,fac[100005];
ll ksm(ll x ,ll n){
ll rec = 1;
while(n){
if (n & 1)
rec = (rec % mod * x % mod) % mod;
x = (x % mod * x % mod )% mod;
n >>= 1;
}
return rec;
}
void makefac(int x){
fac[0] = 1;
for(int i = 1; i <= x; ++i)
fac[i] = (fac[i - 1] % mod * i % mod) % mod;
}
ll getC(int n, int m){
return fac[n] * ksm(fac[m], mod - 2) % mod * ksm(fac[n - m], mod - 2) % mod;
}
void solve(){
scanf("%d%d%d",&n,&m,&k);
if(m + k > n) printf("0\n");
else{
makefac(n + 5);
ll tmp1 = getC(n,k);
ll tmp2 = ksm(2,n);
ll tmp3 = 0;
for(int i = 0; i < m; ++i)
tmp3 = (tmp3 % mod + getC(n,i) % mod) % mod;
ll tmp4 = (tmp2 % mod - tmp3 % mod + mod) % mod;
printf("%lld\n",(tmp1 % mod * ksm(tmp4,mod - 2) % mod) % mod);
}
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
E 赛马
题意:一天小明与他同学准备赛马,他们每人有n匹马,每匹马有一个固定的战力值,战力值高的马会战胜战力值低的马并赢得比赛。每匹马只能出场比赛一次。小明偷看到了他对手每匹马的出场顺序,小明在更改自己马出场顺序后最多能赢多少场比赛。
tips:与先后顺序无关~直接将两个数组排序,然后单调的过一遍自己的马即可。
#include
#define ll long long
using namespace std;
const int maxn = 1005;
int t,n,ans,now,bookmy[1005],book[1005];
void solve(){
scanf("%d",&n);
ans = 0, now = 1;
for(int i = 1; i <= n; ++i)
scanf("%d",&bookmy[i]);
for(int i = 1; i <= n; ++i)
scanf("%d",&book[i]);
sort(bookmy + 1, bookmy + 1 + n);
sort(book + 1, book + 1 + n);
for(int i = 1; i <= n; ++i){
while(bookmy[now] <= book[i] && now <= n)
now++;
if(now > n) break;
if(bookmy[now] > book[i]) ans++,now++;
}
printf("%d\n",ans);
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
F.三角形
题意:小明有一根长度为a的木棒,现在小明想将木棒分为多段(每段木棒长度必须为整数),使得分隔后的木棍中,取出的任意三段都不能构成三角形,小明想知道木棒最多被分成几段?
tips: 要想取得最合适的解,三角形的边长必为fib数列。(任意相邻两项对应两短边,相加恰好等于第三边)。如20,可以分成1 1 2 3 5 8 ,共6段。剩余的长度如果不足,答案不变。(相当于把剩下的长度加到最长的边上。如22,可以分成1 1 2 3 5 10,最多只能分6段)。
要注意a的范围 2^64 - 1,开ll会爆掉(2 ^ 63),所以要用unsigned long long ,读入时使用%llu占位符!
#include
#define ll unsigned long long
using namespace std;
ll a,ans,now,last,rec;
int t;
void solve(){
scanf("%llu",&a);
now = 2, last = 1, ans = 2;
if(a == 1){
printf("1\n");
return ;
}
if(a == 2 || a == 3) printf("2\n");
else{
a -= 2;
while(a >= now){
a -= now;
ans++;
rec = now;
now += last;
last = rec;
}
printf("%llu\n",ans);
}
}
int main(){
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
H.直线
题意:平面上存在n条直线。请问n条直线在平面上最多存在多少交点。
tips:n条直线两两相交,最多有n(n-1) / 2个交点。 n的范围达到1e15,要用大数计算。
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int t = cin.nextInt();
while(t-- > 0) {
BigInteger n = cin.nextBigInteger();
BigInteger tmp = n.multiply(n.subtract(BigInteger.valueOf(1)));
System.out.println(tmp.divide(BigInteger.valueOf(2)));
}
}
}
J 最大值
题意:有一个字符串s,对于字符串中一个非前缀子串恰好为字符串的前缀我们称之为ac串。请问给出一个字符串他的ac串最大长度为多少。
tips:AC串其实是kmp中next数组的含义,所以求出字符串的next数组即可得到答案。(我这里求的是D数组,遍历D数组,最大值即为答案。)
#include
using namespace std;
int t,ans;
char str[100005];
int d[100005];
void Getd(const char *p,int d[] ){
int i = 1, j = 0, np = strlen(p);
while(i < np){
if(p[j] == p[i])
d[i++] = ++j;
else{
if(j > 0) j = d[j-1];
else i++;
}
}
}
int main(){
scanf("%d",&t);
while(t--){
memset(d,0,sizeof(d));
getchar();
scanf("%s",str);
Getd(str, d);
int len = strlen(str);
ans = 0;
for(int i = 0; i < len; ++i){
int tmp = d[i];
ans = max(ans,tmp);
}
printf("%d\n",ans);
}
return 0;
}