2018-2019 ACM-ICPC, Asia Jiaozuo Regional Contest B,D,F

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;
}

思路

原文地址:https://www.cnblogs.com/xxrlz/p/11649793.html