北京信息科技大学第11届程序设计基本技能比赛
A. 太阳花田
题意:一个圆和一个长为x,高为y的矩形最大交集是多少?
题解:分类讨论,显然四种情况,不妨假设x是短边。第一种,圆被矩形完全包含,\(r<x/2\),答案是圆的面积。第二种,圆露出去了两边\(r<y/2\),这里可以一个圆减去一个扇形,但是要加回来两个三角形,三角形的部分是简单三角函数计算。第三种就是露出去四个部分\(r<sqrt(x/2*x/2+y/2*y/2)\),同样是减去四个扇形,加回来四个三角形。最后一种情况,全部包含。
#include <bits/stdc++.h> //万能头
double x, y, r;
const double pi = acos(-1.0); //高精度pi
using namespace std;
int main(){
cin>>x>>y>>r;
if(y<x)swap(x,y);
if(r<=x/2){
printf("%.2f",pi*r*r);
}else if (r<y/2){
double ang = 2*acos(0.5*x/r);
double l = ang*r;
double s = l*r;
s-=x*tan(ang/2)*x/2;
printf("%.2f",pi*r*r-s);
}else if(r*r<(x/2)*(x/2)+(y/2)*(y/2)){
double ang=2*acos(0.5*x/r);
double l=ang*r;
double s=l*r;
s-=x*tan(ang/2)*x/2;
double ang1=2*acos(0.5*y/r);
double l1=ang1*r;
double s1=l1*r;
s1-=y*tan(ang1/2)*y/2;
printf("%.2f",pi*r*r-s-s1);
}else{
printf("%.2f",x*y);
}
return 0;
}
B. 算数教室
题意:给两个长度为n的数组,是否存在两个下标a[i]+b[j]=target。
题解:set存一下a[i],对b中每一个元素,查询一下set中是否有target-b[i]即可(即查询是否存在b[j]=target-a[i]),这样复杂度能够接受。
#include<bits/stdc++.h>
using namespace std;
int n, m, x;
set<int> S;
int main(){
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;++i)S.insert(x);
for(int i=1;i<=m;++i){
int t;scanf("%d",&t);
if(S.count(x-t)){
cout<<"YES";
return 0;
}
}
cout<<"NO";
return 0;
}
C. 博丽神社
题意:过原点的直线有多少条,能使得所有的点与关于这条之间对称。
题解:检查过每个点,和每两个点之间的中垂线。去重可以用pair<int,int>。这表示一个方向向量,细节看代码。
#include<bits/stdc++.h>
#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
inline int read(){
int res=0, f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res*f;
}
const int N = 105;
const int MOD = 1000000007;
template<typename T>
void chmin(T& a, T b){if(a>b)a=b;}
template<typename T>
void chmax(T& a, T b){if(b>a)a=b;}
int n;
struct point{
int x, y;
point(){}
point(int _x, int _y){
x=_x,y=_y;
}
};
point pts[N];
point operator-(point a, point b){
return point(a.x-b.x,a.y-b.y);
}
point operator+(point a, point b){
return point(a.x+b.x,a.y+b.y);
}
const double eps = 1e-8;
set<PII> isExist;
int vis[N];
LL dot(point a, point b){
return 1ll*a.x*b.x+1ll*a.y*b.y;
}
double L(point a){
return sqrt(1.0*a.x*a.x+1.0*a.y*a.y);
}
bool check(point v){
memset(vis,0,sizeof vis);
for(int i=1;i<=n;++i){
if(vis[i])continue;
for(int j=1;j<=n;++j){
if(vis[j])continue;
point m=pts[i]+pts[j];
point d=pts[i]-pts[j];
if(dot(d,v)!=0)continue;
else if(abs(abs(dot(m,v))-L(m)*L(v))>eps)continue;
else{
vis[i]=vis[j]=1;
}
}
}
bool ok=1;
for(int i=1;i<=n;++i)if(!vis[i])ok=0;
return ok;
}
int main(){
n=read();
for(int i=1;i<=n;++i){
int xx=read(),yy=read();
pts[i]=point(xx,yy);
}
int res=0;
for(int i=1;i<=n;++i){
int xx=pts[i].x,yy=pts[i].y;
if(xx&&yy){
int g=__gcd(xx,yy);
xx/=g;yy/=g;
}else{
if(!xx)yy=1;
else xx=1;
}
if(isExist.count(MP(xx,yy)))continue;
if(check(point(xx,yy)))++res;
isExist.insert(MP(xx,yy));
}
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
point m=pts[i]+pts[j];//中点都被放大两倍
point v=pts[i]-pts[j];
if(dot(m,v)!=0)continue;
else{
int& xx=m.x,&yy=m.y;
if(!xx&&!yy)xx=v.y,yy=-v.x;
if(xx&&yy){
int g=__gcd(xx,yy);
xx/=g;yy/=g;
}else{
if(!xx)yy=1;
else xx=1;
}
if(isExist.count(MP(xx,yy)))continue;
if(check(m)){
++res;
isExist.insert(MP(xx,yy));
}
}
}
}
cout<<res;
return 0;
}
D. 小小的百鬼夜行
题意:把一个字符串复制k次,然后看其中出现最多的字母出现次数。
题解:先数一下每一个字符出现次数,然后乘以k,要注意会爆int,开long long。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int res=0, f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res*f;
}
const int MAXN = 200005;
int buk[26];
char s[MAXN];
int n, k;
int main(){
n=read(),k=read();
scanf("%s",s+1);
for(int i=1;i<=n;++i)++buk[s[i]-'a'];
int mx=*max_element(buk,buk+26);
cout<<1ll*mx*k;
return 0;
}
E. 贪玩的二小姐
题意:每次翻转长度为m的区间,并且翻转的区间左端点一直往后,每次翻转输出和。
题解:可以双端队列,但是蒟蒻只会抄板子。splay维护区间,不会的可以去luogu刷一刷模板题。
#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
const int inf = 1e8;
#define ls(x) (bst[x].ch[0])
#define rs(x) (bst[x].ch[1])
#define getKey(x) (bst[x].key)
#define getSize(x) (bst[x].sz)
#define getFather(x) (bst[x].f)
typedef long long LL;
struct splayNode{
int ch[2];
int f;
int sz;
int key;
int rev;
LL sum;
void clear(){ch[0]=ch[1]=f=rev=0;}
}bst[N];
inline bool getDir(int x){return rs(getFather(x))==x;}
int rt, cntNode, top, a[N], id[N], rub[N], n, m, q;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int getId(){//防止节点过多,我们需要设置一个垃圾堆,有废弃的节点应该优先使用
if(top==0)return ++cntNode;
return rub[top--];
}
void changeRev(int nd){
swap(ls(nd),rs(nd));
bst[nd].rev^=1;
}
void pushup(int nd){
splayNode& x=bst[bst[nd].ch[0]], &y=bst[bst[nd].ch[1]];
splayNode& res=bst[nd];
int val=bst[nd].key;
res.sum=x.sum+y.sum+val;
res.sz=x.sz+y.sz+1;
}
void pushdown(int nd){
if(bst[nd].rev){
changeRev(ls(nd));
changeRev(rs(nd));
bst[nd].rev=0;
}
}
void rot(int nd){
int fa=getFather(nd),gf=getFather(fa);
int ws=getDir(nd),ws1=getDir(fa);
int nd1=bst[nd].ch[ws^1];
bst[fa].ch[ws]=nd1;bst[nd1].f=fa;
bst[gf].ch[ws1]=nd;bst[nd].f=gf;
bst[nd].ch[ws^1]=fa;bst[fa].f=nd;
pushup(fa);pushup(nd);
}
void splay(int nd, int goal=0){
while(getFather(nd)!=goal){
int fa=getFather(nd);
int gf=getFather(fa);
if(gf!=goal){
if(getDir(nd)==getDir(fa))rot(fa);
else rot(nd);
}
rot(nd);
}
if(!goal)rt=nd;
}
int kth(int k){
int nd=rt;
while(1){
pushdown(nd);
if(getSize(ls(nd))>=k)nd=ls(nd);
else if(k==getSize(ls(nd))+1)return nd;
else{
k-=getSize(ls(nd))+1;
nd=rs(nd);
}
}
}
int split(int s, int len){//拆出响应的区间
int x=kth(s),y=kth(s+len+1);
splay(x,0);splay(y,x);
return ls(y);
}
void newNode(int nd, int x){//newc出一新节点
bst[nd].sum=x;
bst[nd].rev=0;
bst[nd].sz=1;
}
void build(int s, int e, int fa){
int mid=(s+e)>>1;int curNode=id[mid],pre=id[fa];
if(s==e){
newNode(curNode,a[s]);
}
if(s<mid)build(s,mid-1,mid);
if(mid<e)build(mid+1,e,mid);
bst[curNode].key=a[mid];bst[curNode].f=pre;
pushup(curNode);
bst[pre].ch[mid>=fa]=curNode;
}
void reverseTree(int x, int len){
int nd=split(x,len),y=getFather(nd);
changeRev(nd);
pushup(y);pushup(getFather(y));
}
LL query(int x, int len){//求和
int nd=split(x,len);
return bst[nd].sum;
}
int main(){
n=read(),m=read();
a[1]=a[n+2]=-inf;
for(int i=2;i<=n+1;++i)a[i]=read();
for(int i=1;i<=n+2;++i)id[i]=i;
build(1,n+2,0);
rt=(n+3)>>1;cntNode=n+2;
q = read();
while(q--){
int s = read();
cout<<query(s,m);
cout<<" \n"[q==0];
reverseTree(s,m);
}
return 0;
}
F. 帕秋莉,go!
题意:给一些字符串,构造字典序最大的回文串。
题解:显然,奇数个的只能有一个,选最大的放中间,其他的偶数个按照从大到小从两端开始放就能通过。
#include<bits/stdc++.h>
#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()
using namespace std;
typedef long long LL;
typedef vector<int> VI;
inline int read(){
int res=0, f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res*f;
}
const int N = 200005;
int buk[26], n;
char str[N];
int main(){
n=read();
scanf("%s", str+1);
for(int i=1;i<=n;++i)++buk[str[i]-'a'];
VI s, e;
for(int i=25;i>=0;--i){
for(int id=1;id<=buk[i]/2;++id){
s.PB(i);
e.PB(i);
}
buk[i]= buk[i]&1;
}
reverse(all(e));
string res;
for(int i=0;i<SZ(s);++i)res+=s[i]+'a';
for(int i=25;i>=0;--i){
if(buk[i]){
res+=i+'a';
break;
}
}
for(int i=0;i<SZ(e);++i)res+=e[i]+'a';
cout<<SZ(res)<<endl;
cout<<res;
return 0;
}
G. 三月精和子序列
题意:取三个不同角标,\(i,j,k\),满足\(i<j<k\),且\(s[i],s[j],s[k]\)互不相等。
题解:对每一个字母分别考虑,枚举字母,然后把字母出现次数乘起来就可以了,注意取模。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
inline int read(){
int res=0, f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res*f;
}
const int N = 1000005;
const int MOD = 1000000007;
int buk[26];
int n;
char s[N];
int main(){
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;++i)++buk[s[i]-'a'];
int ans=0;
for(int i=0;i<26;++i){
for(int j=i+1;j<26;++j){
for(int k=j+1;k<26;++k){
ans+=(1ll*buk[i]*buk[j]*buk[k])%MOD;
ans%=MOD;
}
}
}
cout<<ans;
return 0;
}
H. 小町的烦恼
题意:初始值x,每一天这个值都变成前一天的平方倍。
题解:答案是\(x^{2^{n}}\)。考虑快速幂和欧拉降幂。分母的模数应该取\(MOD-1\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline LL read(){
LL res=0, f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
return res*f;
}
LL x, n;
LL powmod(LL x, LL y, LL MOD){
LL res=1;
while(y){
if(y&1)
res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
int main(){
x=read(),n=read();
int MOD = 1e9+7;
cout<<powmod(x%MOD,powmod(2,n-1,MOD-1),MOD);
return 0;
}