第一次写省选的题解,不喜勿喷哈!偶也是蒻蒻!
-----------------------------------------------------------------------------------------正文----------------------------------------------------------------------------------------------------------------------------------------
1.行星序列
题目描述 Description
“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?
输入描述 Input Description
第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,…………,an(0<=ai<=100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值
其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格
输出描述 Output Description
对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和
样例输入 Sample Input
7 43 1 2 3 4 5 6 7 5 1 2 5 5 3 2 4 2 3 7 9 3 1 3 3 4 7
样例输出 Sample Output
2 35 8
数据范围及提示 Data Size & Hint
100%的数据中,M,N<=100000
40%的数据中,M,N<=10000
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
那就肯定是线段树了!
当然,对于想编者(我啦!)这样的弱菜,肯定是想用模拟啦!
只可惜只有40分,那我用树状数组总可以了吧?对不起,60分,好吧,还是线段树吧!
#include <cstdio>
#include <cstring>
#define MAXN 100001
struct node {
int l,r;
long long s,M,A;
node (){
M=1;
A=0;
}
} T[MAXN*4];
int a[MAXN];
int n,p,m;
void Build(int l,int r,int t) { //构造线段树
T[t].l=l;
T[t].r=r;
if (l==r) {
T[t].s=a[l]%p;
return ;
}
Build(l,(l+r)>>1,t<<1);
Build(((l+r)>>1)+1,r,(t<<1)^1);
T[t].s=(T[t<<1].s+T[(t<<1)^1].s)%p;
}
void Change(int l,int r,long long k,int t,int flag) {
if (flag==1) { //如果是加法,就这样做
if (l==T[t].l&&r==T[t].r) {
T[t].M=T[t].M*k;
T[t].M%=p;
if (!T[t].M) T[t].M=p;
T[t].s=T[t].s*k;
T[t].s%=p;
T[t].A=T[t].A*k;
T[t].A%=p;
return ;
}
int mid=(T[t].l+T[t].r)>>1;
if (T[t].M>1) {
Change(T[t].l,mid,T[t].M,t<<1,1);
Change(mid+1,T[t].r,T[t].M,(t<<1)^1,1);
T[t].M=1;
}
if (T[t].A) {
Change(T[t].l,mid,T[t].A,t<<1,2);
Change(mid+1,T[t].r,T[t].A,(t<<1)^1,2);
T[t].A=0;
}
if (r<=mid) Change(l,r,k,t<<1,1)
; else if (l>mid) Change(l,r,k,(t<<1)^1,1)
; else {
Change(l,mid,k,t<<1,1);
Change(mid+1,r,k,(t<<1)^1,1);
}
} else { //乘法
if (l==T[t].l&&r==T[t].r) {
long long ret=(r-l+1)*k;
ret%=p;
T[t].s=T[t].s+ret;
T[t].s%=p;
T[t].A=T[t].A+k;
T[t].A%=p;
return ;
}
int mid=(T[t].l+T[t].r)>>1;
if (T[t].M>1) {
Change(T[t].l,mid,T[t].M,t<<1,1);
Change(mid+1,T[t].r,T[t].M,(t<<1)^1,1);
T[t].M=1;
}
if (T[t].A) {
Change(T[t].l,mid,T[t].A,t<<1,2);
Change(mid+1,T[t].r,T[t].A,(t<<1)^1,2);
T[t].A=0;
}
if (r<=mid) Change(l,r,k,t<<1,2)
; else if (l>mid) Change(l,r,k,(t<<1)^1,2)
; else {
Change(l,mid,k,t<<1,2);
Change(mid+1,r,k,(t<<1)^1,2);
}
}
T[t].s=(T[t<<1].s+T[(t<<1)^1].s);
T[t].s%=p;
}
long long Sum(int l,int r,int t) { //求和
if (l==T[t].l&&r==T[t].r) return T[t].s;
int mid=(T[t].l+T[t].r)>>1;
long long rec;
if (r<=mid) rec=Sum(l,r,t<<1)
; else if (l>mid) rec=Sum(l,r,(t<<1)^1)
; else rec=(Sum(l,mid,t<<1)+Sum(mid+1,r,(t<<1)^1)),rec%=p;
rec=(rec*T[t].M);
rec%=p;
long long ret=T[t].A*(r-l+1);
rec=(rec+ret);
rec%=p;
return rec;
}
int main() {
scanf("%d%d",&n,&p);
for (int i=0;i++<n;) scanf("%d",&a[i]);
Build(1,n,1);
scanf("%d",&m);
while (m--) {
int x,l,r,k;
scanf("%d",&x);
switch (x) { //以下是分类讨论...
case 1:
scanf("%d%d%d",&l,&r,&k);
Change(l,r,k,1,1);
break;
case 2:
scanf("%d%d%d",&l,&r,&k);
Change(l,r,k,1,2);
break;
case 3:
scanf("%d%d",&l,&r);
printf("%lld\n",Sum(l,r,1));
}
}
return 0;
}
2.飞行棋
题目描述 Description
在经过地“小小宇航员夏令营”的学习以及模拟飞行实验后,小可可明白宇航员并不是那么容易当的,除了需要强健的身体,丰富的经验以及灵活的应变能力以外,缜密的思维也是不可少的,为了早日实现自己的宇航员的梦想,小可可决定在平时就开始锻炼——利用棋类游戏来锻炼自己的思维。
小可可发明一种飞行棋,棋盘是一个圆周形,在圆周形上有若干个点,已知这些点与点之间的弧长,弧长均为正整数,并且依圆弧顺序排列,飞行棋的规则是找出这些点中有没有可以围成矩形的,在最短时间内找出所有不重复矩形的玩家胜出。
输入描述 Input Description
第一行为正整数N,表示棋盘上点的个数,
接下来n行分别为这N个点所分割的各个圆弧的长度。
输出描述 Output Description
所构成的不重复的矩形。
样例输入 Sample Input
8
1
2
2
3
1
1
3
3
样例输出 Sample Output
3
数据范围及提示 Data Size & Hint
对于100%数据,0<N<=100,0<每条弧长<1000
这题嘛,明显是数学题!对了,就是数学题,我们知道根据矩形的性质,圆周上的矩形对角线一定是直径,所以只要数出直径的条数n,矩形数就是n*(n-1)/2了。
不就结束了吗?
#include<iostream>
using namespace std;
int n,tot,a[101],sum[101],j,i;
void find_d_number(){ //寻找直径
for(i=1;i<=n;i++)
for(j=0;j<=n;j++)
if(sum[i+j]-sum[i-1]==tot)ans++;
ans/=2; //注意!!!!这里原来的ans并不是直径的最终结果,我们每次多算了一次,故除以2
cout<<ans*(ans-1)/2; //输出矩形数
}
int main(){
for(i=1;i<=n;i++){scanf("%d",&a[i]);tot+=a[i];sum[i]=sum[i-1]+a[i];} //计算和
for(i=1;i<=n;i++)sum[i+n]=sum[i+n-1]+a[i];
tot/=2;
find_d_ans();
return 0;
}
3.中国象棋
题目描述 Description
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入描述 Input Description
一行包含两个整数N,M,之间由一个空格隔开。
输出描述 Output Description
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
样例输入 Sample Input
1 3
样例输出 Sample Output
7
数据范围及提示 Data Size & Hint
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
很多人当时在考试的时候都想到了DP,但是,就是不会转移方程,就是推不出来,有的大犇就比较NB了,居然打表对了3个点,以后要多多学习!(PS:更有甚者竟然搜索对了几个点!!!)
现在给出DP转移方程:
初始化:
f[0][0][0]=1;
还要说了吗?上程序
code:
#include<stdio.h> #define mod 9999973 long long f[110][110][110],ans; inline long long C(long long x){return x*(x-1)/2;} int n,m; int main() { int i,j,k; scanf("%d%d",&n,&m); f[0][0][0]=1; for (int i=1;i<=n;i++) for (int j=0;j<=m;j++) for (int k=0;k<=m-j;k++) {f[i][j][k]=f[i-1][j][k]; if (j) f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1); if (k&&j<m) f[i][j][k]+=f[i-1][j+1][k-1]*(j+1); if (j>1) f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2); if (j&&k) f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)*j; if (k>1&&j<m-1) f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2); if (f[i][j][k]>=mod) f[i][j][k]%=mod; } for(int i=0;i<=m;i++)for(int j=0;j<=m-i;j++) ans+=f[n][i][j]; if(ans>=mod) ans%=mod; printf("%lld\n",ans); }