Problem B. Maximal Difference (Junior)
题意:给你一个n,让你找出n位数中各个位数相加相等且差值最大的值。 比如n=2就是91和19,所以答案就是72
题解:直接一个个算出来或者找规律,n=2就是91和19,n=3就是910和109,n=4就是9910和1099。
代码:
#include<cstdio>
int n;
int main(){
scanf("%d",&n);
switch(n){
case 1: printf("0\n");break;
case 2: printf("72\n");break;
case 3: printf("801\n");break;
case 4: printf("8811\n");break;
case 5: printf("89001\n");break;
case 6: printf("898101\n");break;
case 7: printf("8990001\n");break;
case 8: printf("89981001\n");break;
case 9: printf("899900001\n");break;
case 10: printf("8999810001\n");break;
}
return 0;
}
Problem D. Triangle Construction (Junior)
题意:给你三角形的三边,若不能成为三角形则输出Impossible ,否则输出三个点的坐标(任意)。
题解:固定两个点在x轴上,然后求出一个角的角度,用三角形正余弦定理就能搞定了= =
代码:
#include<cstdio>
#include<cmath>
const double eps=1e-7;
struct node{
double x,y;
}e[3];
double a,b,c;
double s;
int main(){
scanf("%lf%lf%lf",&a,&b,&c);
if(a+b-c<=eps||a+c-b<=eps||b+c-a<=eps){
printf("Impossible\n");
return 0;
}
e[0].x=e[0].y=0;
e[1].x=a;
e[1].y=0;
double s=acos((b*b+a*a-c*c)/(double)(2*a*b));
printf("%lf %lf\n",e[0].x,e[0].y);
printf("%lf %lf\n",e[1].x,e[1].y);
printf("%lf %lf\n",a-b*cos(s),b*sin(s));
return 0;
}
Problem F. Beautiful Patterns (Junior)
题意:给你一个n*m的地板和k块已经知道颜色的单元地板,若是这个地板在所有的2*2的块中都有3个白地板和1个黑地板或3个黑地板和1个白地板,则这个n*m的地板则为beautiful,求有多少种方法能使得这个n*m的地板为beautiful
题解:由于n,m较小,因此我们先枚举第一行的所有情况,最多有1024种情况(要排除不行的,题目给你是白色的,你却用黑色填充这种情况)。然后对于下一行,我们假设第一块是白色或者黑色的可能,然后从第二块开始枚举,就可以得到精确的这一行各块的颜色了,我们用mp[i][j]记录方法到第i行第j种方法的方案数,最后我们会得到这么一个递推公式:mp[i][ss]=(mp[i][ss]+mp[i-1][j])%mod。最后ans=∑mp[n-1][0->2^(m-1)]
代码:
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll n,m,k,x,y,z,flag,ss;
ll mp[15][1111],mmp[15][15];
ll s[15][15],ans,sa[15];
struct node{
ll x,y,z;
}e[105];
int main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=0;i<=10;i++)
for(ll j=0;j<=10;j++) s[i][j]=-1;
for(ll i=0;i<k;i++){
scanf("%lld%lld%lld",&x,&y,&z);
s[x-1][y-1]=z;
}
sa[0]=1;
for(ll i=1;i<=10;i++) sa[i]=sa[i-1]*2;
for(ll i=0;i<sa[m];i++){
flag=1;
for(ll j=0;j<m;j++){
if(s[0][j]!=-1&&((i>>j)%2)!=s[0][j]){
flag=0;
break;
}
}
if(flag) mp[0][i]=1;
}
for(ll i=1;i<n;i++){
for(ll j=0;j<sa[m];j++){
if(mp[i-1][j]==0) continue;
for(ll k=0;k<m;k++) mmp[i-1][k]=(j>>k)%2;
mmp[i][0]=0; //最前面为0的情况
for(ll k=1;k<m;k++){
ll a0=0,a1=0;
if(mmp[i][k-1]==0) a0++;
if(mmp[i-1][k-1]==0) a0++;
if(mmp[i-1][k]==0) a0++;
if(a0==0||a0==2) mmp[i][k]=0;
else mmp[i][k]=1;
}
flag=1;
for(ll k=0;k<m;k++){
if(s[i][k]!=-1&&s[i][k]!=mmp[i][k]){
flag=0;
break;
}
}
if(flag){
ss=0;
for(ll k=m-1;k>=0;k--) ss=ss*2+mmp[i][k];
mp[i][ss]=(mp[i][ss]+mp[i-1][j])%mod;
}
mmp[i][0]=1; //最前面为1的情况
for(ll k=1;k<m;k++){
ll a0=0,a1=0;
if(mmp[i][k-1]==0) a0++;
if(mmp[i-1][k-1]==0) a0++;
if(mmp[i-1][k]==0) a0++;
if(a0==0||a0==2) mmp[i][k]=0;
else mmp[i][k]=1;
}
flag=1;
for(ll k=0;k<m;k++){
if(s[i][k]!=-1&&s[i][k]!=mmp[i][k]){
flag=0;
break;
}
}
if(flag){
ss=0;
for(ll k=m-1;k>=0;k--) ss=ss*2+mmp[i][k];
mp[i][ss]=(mp[i][ss]+mp[i-1][j])%mod;
}
}
}
for(ll i=0;i<sa[m];i++) ans=(ans+mp[n-1][i])%mod;
printf("%lld\n",ans);
return 0;
}
Problem H. String without repetitions (Junior)
题意:让你求一个长度为n的字符串,长度为k的任意两个相邻子串都不能相同(k为任意),且字典序最小。
题解:由于任意两个相邻子串都不能相同,所以我们要分割这个串,因此我们能先把1的倍数的位置先放入a,然后在2的倍数的位置放入b,再在4的倍数的位置放入c,按照这个规律即可求出这个长度为n的字符串。
代码:
#include<cstdio>
#include<cstring>
int n,a[30],s;
char st[4000005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) st[i]='a';
a[0]=1;
for(int i=1;i<30;i++){
a[i]=a[i-1]*2;
if(a[i]>=n){
s=i;break;
}
}
for(int i=1;i<=s;i++){
for(int j=a[i];j<=n;j+=a[i]){
st[j]='a'+i;
}
}
printf("%s\n",st+1);
return 0;
}
Problem J. Beans gathering (Junior)
题意:给你n个盒子和每个盒子中豆子的个数,第i个可以将i的倍数个豆子放入第i-1个,问你最后能不能把所有豆子放到第0个盒子
题解:从后向前for一遍就可以了= =,如果遇到一个盒子的豆子无法被i除尽,则输出no,否则将这些豆子放到第i-1个盒子。
代码:
#include<cstdio>
#include<cstring>
int n,a[30],s;
char st[4000005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) st[i]='a';
a[0]=1;
for(int i=1;i<30;i++){
a[i]=a[i-1]*2;
if(a[i]>=n){
s=i;break;
}
}
for(int i=1;i<=s;i++){
for(int j=a[i];j<=n;j+=a[i]){
st[j]='a'+i;
}
}
printf("%s\n",st+1);
return 0;
}
Problem L. Reverse beans gathering (Junior)
题意:给你n个豆子,让你放在各个盒子里,移动方法如上题,问你输出最小字典序的豆子放法(最小字典序定义看题)
题解:将n个豆子从第0个盒子到1,2,3模拟,直到豆子用完为止即可。
例: 7个豆子。
7%1==0,所以变成0 7
7%2==0,所以变成0 1 6
6%3==0,所以变成0 1 0 6
6%4==2,所以变成0 1 0 2 4 到此豆子用完。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,a[100005],flag=1;
int main(){
scanf("%lld",&n);
for(ll i=0;i<=n;i++) scanf("%lld",&a[i]);
for(ll i=n;i>0;i--){
if(a[i]%i==0) a[i-1]+=a[i];
else{
flag=0;
break;
}
}
if(flag) printf("Yes\n");
else printf("No\n");
return 0;
}
Problem N. Equation (Junior)
题意:给你多项式的系数,求满足等式的最小x
题解:这题可以转化为,是否存在x满足多项式是p*2的倍数,那就可以枚举x,由于循环节是2014*2014,所以最多循环2014*2014次。
代码:
#include<bits/stdc++.h>
typedef long long ll;
ll n,a[25],p,flag,ans;
int main(){
scanf("%lld%lld",&p,&n);
for(ll i=n;i>=0;i--) scanf("%lld",&a[i]);
ll pp=p*p;
for(ll i=0;i<=pp;i++){
ll sum=0,s=1;
for(ll j=0;j<=n;j++){
sum=(sum+s*a[j])%pp;
s=(s*i)%pp;
}
if(sum==0){
ans=i;
flag=1;
break;
}
}
if(flag) printf("%lld\n",ans);
else printf("-1\n");
return 0;
}
Problem P. Competition (Junior)
题意:给你两个数列,他们的数字是1->2*n,每个数都不相同,然后每次第一个人从第一个数列选一个数,第二个人从第二个数列选一个数,问第二个人能赢的次数最多是多少次。
题解:把第二个人的数放入数组,然后贪心即可(不会的可以去看看田忌赛马)
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,a[400005],x,sum,ans;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&x);
for(int i=0;i<n;i++){
scanf("%d",&x);
a[x]=1;
}
for(int i=1;i<=2*n;i++){
if(a[i]==0) sum++;
else{
if(sum>0){
ans++;
sum--;
}
}
}
printf("%d\n",ans);
return 0;
}
Problem R. The incircle (Junior)
题意:给你一个凸多边形,求内含的圆的最大半径。
题解:半平面交模板题,首先二分边向内延伸的长度,对于每个长度,我们通过半平面交,判断是否存在内核,若存在内核,则存在该半径(也就是向内延伸的长度),增大半径,反之亦然。 代码:
#include<iostream>
#include <stdio.h>
#include <math.h>
#define eps 1e-8
using namespace std;
const int MAXN=105;
int m;
double r;
int cCnt,curCnt;//此时cCnt为最终切割得到的多边形的顶点数、暂存顶点个数
struct point
{
double x,y;
};
point points[MAXN],p[MAXN],q[MAXN];//读入的多边形的顶点(顺时针)、p为存放最终切割得到的多边形顶点的数组、暂存核的顶点
void getline(point x,point y,double &a,double &b,double &c){//两点x、y确定一条直线a、b、c为其系数
a = y.y - x.y;
b = x.x - y.x;
c = y.x * x.y - x.x * y.y;
}
void initial(){
for(int i = 1; i <= m; ++i)p[i] = points[i];
p[m+1] = p[1];
p[0] = p[m];
cCnt = m;//cCnt为最终切割得到的多边形的顶点数,将其初始化为多边形的顶点的个数
}
point intersect(point x,point y,double a,double b,double c){//求x、y形成的直线与已知直线a、b、c、的交点
double u = fabs(a * x.x + b * x.y + c);
double v = fabs(a * y.x + b * y.y + c);
point pt;
pt.x=(x.x * v + y.x * u) / (u + v);
pt.y=(x.y * v + y.y * u) / (u + v);
return pt;
}
void cut(double a,double b ,double c){
curCnt = 0;
for(int i = 1; i <= cCnt; ++i){
if(a*p[i].x + b*p[i].y + c >= 0)q[++curCnt] = p[i];// c由于精度问题,可能会偏小,所以有些点本应在右侧而没在,
//故应该接着判断
else {
if(a*p[i-1].x + b*p[i-1].y + c > 0){//如果p[i-1]在直线的右侧的话,
//则将p[i],p[i-1]形成的直线与已知直线的交点作为核的一个顶点(这样的话,由于精度的问题,核的面积可能会有所减少)
q[++curCnt] = intersect(p[i],p[i-1],a,b,c);
}
if(a*p[i+1].x + b*p[i+1].y + c > 0){//原理同上
q[++curCnt] = intersect(p[i],p[i+1],a,b,c);
}
}
}
for(int i = 1; i <= curCnt; ++i)p[i] = q[i];//将q中暂存的核的顶点转移到p中
p[curCnt+1] = q[1];p[0] = p[curCnt];
cCnt = curCnt;
}
int solve(){
//注意:默认点是顺时针,如果题目不是顺时针,规整化方向
initial();
/*for(int i = 1; i <= m; ++i){
double a,b,c;
getline(points[i],points[i+1],a,b,c);
cut(a,b,c);
} */
//如果要向内推进r,用该部分代替上个函数
for(int i = 1; i <= m; ++i){
point ta, tb, tt;
tt.x = points[i+1].y - points[i].y;
tt.y = points[i].x - points[i+1].x;
double k = r / sqrt(tt.x * tt.x + tt.y * tt.y);
tt.x = tt.x * k;
tt.y = tt.y * k;
ta.x = points[i].x + tt.x;
ta.y = points[i].y + tt.y;
tb.x = points[i+1].x + tt.x;
tb.y = points[i+1].y + tt.y;
double a,b,c;
getline(ta,tb,a,b,c);
cut(a,b,c);
}
/* //多边形核的面积
double area = 0;
for(int i = 1; i <= curCnt; ++i)
area += p[i].x * p[i + 1].y - p[i + 1].x * p[i].y;
area = fabs(area / 2.0);
*/ if(cCnt<=0)return 0;
return 1;
}
void GuiZhengHua(){
//规整化方向,逆时针变顺时针,顺时针变逆时针
for(int i = 1; i < (m+1)/2; i ++)
swap(points[i], points[m-i]);
}
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%lf%lf",&points[i].x,&points[i].y);
double x1=points[0].x,x2=points[1].x,x3=points[2].x,y1=points[0].y,y2=points[1].y,y3=points[2].y;
if((x2-x1)*(y3-y2)-(y2-y1)*(x3-x2)>0)
{
GuiZhengHua();
} points[m+1] = points[1];
double high = 2000000;
double low = 0,mid;
while(low + eps <= high){
mid = (high + low)/2.0;
r=mid;
if(solve())low = mid;
else high = mid;
}
printf("%lf\n",high);
}
Problem T. The dividing line (Junior)
题意:给你n个点,m条直线,对于每条直线,求是否有两个点在不同侧。
题解:n*m暴力判断点与直线的关系。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll n,m,x1,x2,y1,y2,a,b;
struct node{
ll x,y;
}e[10005];
ll cal(ll x,ll y){
return (y2-y1)*(x-x1)-(y-y1)*(x2-x1);
}
int main(){
scanf("%lld",&n);
for(ll i=1;i<=n;i++) scanf("%lld%lld",&e[i].x,&e[i].y);
scanf("%lld",&m);
while(m--){
a=0;
b=0;
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
for(ll j=1;j<=n;j++){
if(cal(e[j].x,e[j].y)>0&&a==0) a=j;
else if(cal(e[j].x,e[j].y)<0&&b==0) b=j;
if(a!=0&&b!=0){
printf("%lld %lld\n",min(a,b),max(a,b));
break;
}
}
if(a==0||b==0) printf("0\n");
}
return 0;
}
Problem V. Stringangulation (Junior)
题意:给你一个环形的串,求分割三次后,得到a,b,c三个串。问有多少分割满足a+b>c&&a+c>b&&b+c>a(这里是字符串比较与字符串拼接)
题解:n^4暴力分割比较
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int>p[300];
string st;
string a,b,c;
int ans;
int main(){
cin>>st;
for(int i=0;i<st.size();i++) p[st[i]].push_back(i);
for(int i=0;i<256;i++){
if(p[i].size()>=3){
for(int j=0;j<p[i].size();j++){
for(int k=j+1;k<p[i].size();k++){
for(int kk=k+1;kk<p[i].size();kk++){
a.clear();
b.clear();
c.clear();
for(int ss=p[i][j];ss!=p[i][k];ss=(ss+1)%st.size()) a+=st[ss];
for(int ss=p[i][k];ss!=p[i][kk];ss=(ss+1)%st.size()) b+=st[ss];
for(int ss=p[i][kk];ss!=p[i][j];ss=(ss+1)%st.size()) c+=st[ss];
if(a+b>c&&b+c>a&&c+a>b) ans++;
}
}
}
}
}
cout<<ans<<endl;
return 0;
}