A
感觉是个画图题...
当角度过小,b和a+r构不成直角三角形时,(w = b*sin(th) + (a+r) * cos(th)),否则 (w = sqrt{(a+r)^2+b^2})
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
const double eps = 1e-8;
const double pi = acos(-1);
int main(){
int t; scanf("%d",&t);
double a,b,r,th;
while(t--){
scanf("%lf%lf%lf%lf",&a,&b,&r,&th);
th = th/180*pi;
double mp = sqrt((a+r)*(a+r)+b*b);
double t1 = asin(b/mp);
if(t1<=th){
printf("%.12lf
",mp-r);
}else{
double si = sin(th),cs = cos(th);
printf("%.12lf
",(a+r)*cs+b*si-r);
}
}
return 0;
}
D
模拟题
- 思路: 找到蜂巢和连接边的规律来建图,然后跑最短路.
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N = 1e3+10;
const int inf = 0x3f3f3f3f;
char ma[N*5][N*6];
int n,m,sp,tp;
struct E{
int v,nxt;
}e[N*N*100];
int head[N*N],tot,d[N*N];
void init(){
memset(head,-1,sizeof head); tot = 0;
}
void add(int u,int v){
e[tot] = (E){v,head[u]}; head[u] = tot++;
e[tot] = (E){u,head[v]}; head[v] = tot++;
}
int toid(int x,int y){
return (x-1)*m+y;
}
int dij(){
fill(d,d+1+n*m,inf);
priority_queue<pii,vector<pii>,greater<pii> > que;
d[sp] = 0; que.push(pii(0,sp));
while(que.size()){
pii p = que.top(); que.pop();
int u = p.second;
if(d[u]<p.first) continue;
for(int i=head[u];~i;i=e[i].nxt){
int v = e[i].v;
if(d[v] > d[u]+1){
d[v] = d[u] + 1;
que.push(pii(d[v],v));
}
}
}
return d[tp];
}
int main(){
int t; scanf("%d",&t);
char ch;
while(t--){
init();
scanf("%d%d",&n,&m);
getchar();
for(int i=1;i<=n*4+3;++i){
int idx = 1;
do{
ch = getchar();
ma[i][idx++] = ch;
}while(ch!='
');
}
int x,y;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(j%2){
x = i*4-1,y = j*6-1;
if(ma[x-1][y+3]==' '){ // 左上
add(toid(i,j),toid(i-1,j+1));
}
if(ma[x+1][y+3]==' '){ // 左
add(toid(i,j),toid(i,j+1));
}
if(ma[x+2][y]==' '){ // 下
add(toid(i,j),toid(i+1,j));
}
}else{
x = i*4+1,y = j*6-1;
if(ma[x-1][y+3]==' '){ // 左
add(toid(i,j),toid(i,j+1));
}
if(ma[x+1][y+3]==' '){ // 左下
add(toid(i,j),toid(i+1,j+1));
}
if(ma[x+2][y]==' '){ // 下
add(toid(i,j),toid(i+1,j));
}
}
if(ma[x][y]=='S'){sp = toid(i,j);}
if(ma[x][y]=='T'){tp = toid(i,j);}
}
}
int ans = dij();
if(ans!=inf) printf("%d
",ans+1);
else puts("-1");
}
return 0;
}
F
构造题
- 思路: a和b要优先打死一个,先算出来a和b都打死的总天数last.(只要和大于ha+hb,就一定存在一种方案组合出ha和hb),因为要字典序最小,所以构造时尽量先打a
先打a: lower_bound算出打死a的时间pos,然后 sum[pos]-ha 为打a多的攻击力(攻击力对应了一天), 如果sum[last] - sum[pos] >= hb 说明后面可以打死b,否则要用多的攻击力那一天去打b.
先打b: 先打b不能直接全打b,要拿盈余伤害先去打a. 具体看注释吧
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+10;
ll sum[N];
int main(){
int n = 1e5+10;
for(int i=1;i<n;++i){
sum[i] = sum[i-1] + i;
}
int t; scanf("%d",&t);
while(t--){
ll ha,hb,ta,tb;
scanf("%lld%lld%lld%lld",&ha,&hb,&ta,&tb);
ll last = lower_bound(sum+1,sum+n,ha+hb)-sum; // a,b都被打死
string sa,sb;
ll pos = lower_bound(sum+1,sum+n,ha) - sum ; // 打死a的回合
ll los = sum[pos] - ha; // 盈余的伤害
ll s1 = pos*ta + last*tb; // 收到的攻击
if(sum[last] - sum[pos] >= hb){ // 后面之间可以把b打死
for(ll i=1;i<=pos;++i) sa += 'A';
}else{ // 打不死 要在前面穿插一次攻击
for(ll i=1;i<=pos;++i){
if(i!=los) sa+='A';
else sa += 'B';
}
}
for(ll i=pos+1;i<=last;++i) sa+='B';
pos = lower_bound(sum+1,sum+n,hb)-sum; // 打死b的回合
ll s2 = last*ta + pos*tb; // 收到的伤害
if(s1<s2){// 先a更优
printf("%lld ",s1);
cout << sa+'
';
}else{
los = sum[pos] - hb; // 盈余伤害
ll lb1 = upper_bound(sum+1,sum+n,los) - sum - 1;
ll bac = sum[last] - sum[pos]; // 后面能对a造成的伤害
if(bac+sum[lb1]>=ha){// "AAABBBAAA"
for(ll i=1;i<=lb1;++i){
sb += 'A';
}
for(ll i=lb1+1;i<=pos;++i){
sb += 'B';
}
}else{// AB交替 直到后面能打死A
ll left = ha-bac; // 还需要多少能打死A
for(ll i=1;i<=pos;++i){
if(left-i>i || left==i){ // left-i<=i 就说明后面不能构成整数天去打a了,会亏攻击力就不能去打a
left -= i;
sb += 'A';
}else{
sb += 'B';
}
}
}
for(ll i=pos+1;i<=last;++i){
sb += 'A';
}
printf("%lld ",s2);
if(s2<s1) cout << sb +'
';
else cout << min(sa,sb) +'
';
}
}
return 0;
}