La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2017
文章目录
题目链接:http://codeforces.com/gym/101522
G.Gravitational Tetris
题意:类似于俄罗斯方块,10列,给定每列剩余方块数(<=12),通过使用8种不同形状的组块,使每行全部消除
输出总共需要的组块数,以及每行每次的使用型号以及使用位置(组块摆放位置左下角所在列)
题解:因为是一定可以消除的,每个型号都是4块方块组成,10列,只需输出任意一种方案,则只需考虑一种能满足所有可能情况至少需要的消除行数。由此,可假定铺满40行(>=24且能通过1,2,3,4组合皆可;通过使用型号AG使前9列铺满40行,a[i]+4A+3G=40,求满足的一组A,G即可,然后a[i+1]+=G;(G为L字形)
通过分类讨论最后一列的方块个数(除4余2或整除4),用AG铺满38格(除4余2)或40格。对于前者再使用1个E(置于9列),两个B(置于第1,5列)。
记录使用总数,储存使用型号和位置
#include<bits/stdc++.h>
using namespace std;
int a[10],b,c,cnt=0;
vector<char>s;
vector<int>v;
int main(){
for(int i=0;i<10;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<9;i++){//前9列铺满40行
b=(40-a[i])/4;
for(int j=b;j>=0;j--){
if((40-a[i]-j*4)%3==0){
c=(40-a[i]-j*4)/3;
b=j;
break;
}
}
a[i+1]+=c;
a[i]+=4*b+c*3;
for(int j=1;j<=b;j++){
s.push_back('A');
v.push_back(i+1);
}
for(int j=1;j<=c;j++){
s.push_back('G');
v.push_back(i+1);
}
cnt+=c+b;
}
if(a[9]%4==0){//第10列单独讨论
for(int i=1;i<=(40-a[9])/4;i++){
s.push_back('A');
v.push_back(10);
cnt++;
}
}else if(a[9]%4==2){
for(int i=1;i<=(38-a[9])/4;i++){
s.push_back('A');
v.push_back(10);
cnt++;
}
s.push_back('E');
v.push_back(9);
s.push_back('B');
v.push_back(1);
s.push_back('B');
v.push_back(5);
cnt+=3;
}
cout<<cnt<<endl;
for(int i=0;i<s.size();i++){
cout<<s[i]<<" "<<v[i]<<endl;
}
}
H.Hit!
题意:给定两圆圆心坐标、半径,输出任意一点,该点在两圆公共部分内
**题解:**已知两圆,
(
x
−
x
1
)
2
+
(
y
−
y
1
)
2
=
r
1
2
,
,
(
x
−
x
2
)
2
+
(
y
−
y
2
)
2
=
r
2
2
(x-x_1 )^2+(y-y_1 )^2=r_1^2,,(x-x_2 )^2+(y-y_2 )^2=r_2^2
(x−x1)2+(y−y1)2=r12,,(x−x2)2+(y−y2)2=r22
两圆公式相减得一条直线(公共弦或切线),令其与连心线相交的点为P(在两圆相交的公共部分内)
O
1
P
=
λ
P
O
2
(
这
里
表
示
长
度
大
小
,
λ
>
0
)
,
O_1 P=λPO_2(这里表示长度大小,λ>0),
O1P=λPO2(这里表示长度大小,λ>0),
其 中 λ = ∣ ( ( x 1 − x 1 ) 2 + ( y 1 − y 2 ) 2 + ( r 1 2 − r 2 2 ) ) / ( ( x 1 − x 2 ) 2 + ( y − y 2 ) 2 − ( r 1 2 − r 2 2 ) ) ∣ 其中λ=|((x_1-x_1 )^2+(y_1-y_2 )^2+(r_1^2-r_2^2 ))/((x_1-x_2 )^2+(y-y_2 )^2-(r_1^2-r_2^2 ) )| 其中λ=∣((x1−x1)2+(y1−y2)2+(r12−r22))/((x1−x2)2+(y−y2)2−(r12−r22))∣
再以连心线为斜边,平行于x,y轴的直角边的三角形,通过比例线段得到该点的横纵坐标
特别的:当 λ=r1/r2 时,两圆相切,此时P点坐标
p
(
(
x
1
r
1
+
x
2
r
1
)
/
(
r
1
+
r
2
)
,
(
y
1
r
2
+
y
2
r
1
)
/
(
r
1
+
r
2
)
)
p((x_1 r_1+x_2 r_1)/(r_1+r_2 ),(y_1 r_2+y_2 r_1)/(r_1+r_2 ))
p((x1r1+x2r1)/(r1+r2),(y1r2+y2r1)/(r1+r2))
(代入相切情况的坐标公式就符合所有点过了,保险起见可通过比例线段求一般坐标公式)
#include<bits/stdc++.h>
using namespace std;
int xa,ya,ra,xb,yb,rb;
int main(){
cin>>xa>>ya>>ra>>xb>>yb>>rb;
double r=ra+rb;
printf("%.6lf %.6lf",(double)(xa*rb+xb*ra)/r,(double)(ya*rb+yb*ra)/r);
}
I. Inverted Signs
**题意:**一串数,定义混沌指数为相邻两数的差值总和,改变其中连续几位的符号,使得混沌指数最小, 求最小值
题解:显然若改变H[l]…H[r]的符号,混沌指数改变的只有|H[l]-H[l-1]|和|H[r+1]-H[r]|这两项
改变的大小分别为|H[i]+H[i-1]|-|H[i]-H[i-1]|
遍历找出所有负改变(使混沌数减小)的项,选取负改变最多的两项,考虑特殊情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000005;
ll n,h[maxn],sum=0;
vector<ll>v;
int main() {
scanf("%lld",&n);
scanf("%lld",&h[1]);
for(ll i=2; i<=n; i++) {
scanf("%lld",&h[i]);
sum+=abs(h[i]-h[i-1]);
int t=abs(h[i-1]+h[i])-abs(h[i]-h[i-1]);
if(t<0) v.push_back(t);//负改变的项
}
int len=v.size();
if(len==0) return cout<<sum,0;
sort(v.begin(),v.end());
if(len==1) return cout<<sum+v[0],0;
if(len>1) return cout<<sum+v[0]+v[1],0;
}
J . Juicy Candies
**题意:**求由B个‘B’, R个‘R’,S个’S’组成的所有字符串中(相邻字母要求不同) 字典序第k小的字符串,没有:None
**题解:**定义dp [i] [b] [r] [s] 表示以b个’B’,r个’R’,s个’S’,i所代表字母开头的前缀的字符串种数(i=0表示B, 1表示R, 2表示S )
显然 dp [0] [1] [0] [0]=dp [1] [0] [1] [0]= dp [2] [0] [0] [1]=1 有状态转移方程
i=0时:dp[i] [b] [r] [s]=dp [1] [b-1] [r] [s]+dp [2] [b-1] [r] [s]
i=1时:dp[i] [b] [r] [s]=dp [0] [b] [r-1] [s]+dp [2] [b] [r-1] [s]
i=2时:dp[i] [b] [r] [s]=dp [0] [b] [r] [s-1]+dp [1] [b] [r] [s-1]
当 dp[0] [B] [R] [S]+dp [1] [B] [R] [S]+dp [2] [B] [R] [S]<K 时 不存在
找字符串时,遵循:
(1): b<B&c!=‘B’&dp[0] [B-b] [R-r] [S-s]>=K, +‘B’,K-=dp[0] [B-b] [R-r] [S-s] ,Go to (4)
(2): r<R&c!=‘R’&dp[1] [B-b] [R-r] [S-s]>=K, +‘R’,K-=dp[1] [B-b] [R-r] [S-s] Go to (4)
(3): s<S&c!=‘S’&dp[2] [B-b] [R-r] [S-s]>=K, +‘S’, K-=dp[2] [B-b] [R-r] [S-s]
(4): 更新b,r,s,c的值
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e18+5;
ll B,R,S,K;
ll dp[3][205][205][205];//dp[i][b][r][s]以b个B、r个R、s个S、i开头为前缀的字符串的个数
string str;
int main(){
scanf("%lld%lld%lld%lld",&B,&R,&S,&K);
dp[0][1][0][0]=dp[1][0][1][0]=dp[2][0][0][1]=1;
for(int a=0;a<=B;a++){
for(int b=0;b<=R;b++){
for(int c=0;c<=S;c++){
for(int i=0;i<3;i++){//状态转移
if(a&&i==0) dp[i][a][b][c]+=dp[1][a-1][b][c]+dp[2][a-1][b][c];
if(b&&i==1) dp[i][a][b][c]+=dp[0][a][b-1][c]+dp[2][a][b-1][c];
if(c&&i==2) dp[i][a][b][c]+=dp[0][a][b][c-1]+dp[1][a][b][c-1];
if(dp[i][a][b][c]>=K) dp[i][a][b][c]=maxn;
}
}
}
}
if(dp[0][B][R][S]+dp[1][B][R][S]+dp[2][B][R][S]<K) return cout<<"None",0;//字典序第k小的字符串不存在
ll p=-1,b=B,r=R,s=S;
for(int i=1;i<=B+R+S;i++){
for(int j=0;j<3;j++){
if(j!=p){
if(dp[j][b][r][s]>=K){
if(b&&j==0){
b--;
str.push_back('B');
}else if(r&&j==1){
r--;
str.push_back('R');
}else if(s&&j==2){
s--;
str.push_back('S');
}
p=j;
break;
}else K-=dp[j][b][r][s];
}
}
}
cout<<str;
return 0;
}
K .Knights
**题意:**N*M矩形,同行或同列已被占领的格子中间的所有格子都会被占领,求在给定已占领格子的基础上占领所有格子还至少需要派骑士占领多少个格子;
**题解:**4个角必须单独派骑士占领,同时4个角占领,整个矩形也被占领,所以至少需要4个
考虑特殊情况 1*1:至少1个; 一行或一列:至少两个 其他:4个 ; 扣除给定的角
#include<bits/stdc++.h>
using namespace std;
int n,m,k,x,y,cnt=0;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
scanf("%d%d",&x,&y);
if(x==1&&y==1) cnt++;
else if(x==1&&y==m) cnt++;
else if(x==n&&y==1) cnt++;
else if(x==n&&y==m) cnt++;
}
if(n==1&&m==1) return cout<<1-cnt,0;
if(n==1||m==1) return cout<<2-cnt,0;
return cout<<4-cnt,0;
}
L .Let Me Count The Ways
题意: 给定两行,分别n列,m列(n>=m), 将n+m个数放入这m+n个格子,使得数字不重复、每行前面的数比后面的数大,每列第一行的数比第二行的数大,,,,求有几种放法。对1e9+7取模
题解: 答案为第一行按照大小选取n个数C(m+n,n),减掉不符合列大小规则的数量C(n+m,n+1);
即 c(m+n,n) - C(n+m,n+1)
这里逆元,Lucas定理,存在问题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=1e9+7;
ll n,m;
ll quick_mod(ll a, ll b) {
ll ans = 1;
a %= p;
while(b) {
if(b & 1) {
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
ll C(ll n, ll m) {
if(m > n) return 0;
ll ans = 1;
for(int i=1; i<=m; i++) {
ll a = (n + i - m) % p;
ll b = i % p;
ans = ans * (a * quick_mod(b, p-2) % p) % p;
}
return ans;
}
ll Lucas(ll n, ll m) {
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
int main() {
scanf("%lld%lld", &n, &m);
printf("%lld", Lucas(m+n,n)-Lucas(m+n,n+1));
return 0;
}