牛客国庆集训派对Day1 - C、J、L

C - Utawarerumono

题目描述

算术是为数不多的会让Kuon感到棘手的事情。通常她会找Haku帮忙,但是Haku已经被她派去买东西了。于是她向你寻求帮助。
给出一个关于变量x,y的不定方程ax+by=c,显然这个方程可能有多个整数解。Kuon想知道如果有解,使得p2*x2+p1*x+q2*y2+q1*y最小的一组整数解是什么。为了方便,你只需要输出p2*x2+p1*x+q2*y2+q1*y的最小值。

输入描述:

第一行三个空格隔开的整数a,b,c(0 ≤ a,b,c≤ 105)。
第二行两个空格隔开的整数p1,p2(1 ≤ p1,p2 ≤ 105)。
第三行两个空格隔开的整数q1,q2(1 ≤ q1,q2 ≤ 105)。

输出描述:

如果方程无整数解,输出“Kuon”。
如果有整数解,输出p2*x2+p1*x+q2*y2+q1*y的最小值。

 

示例1

输入

复制

2 2 1
1 1
1 1

输出

复制

Kuon

示例2

输入

复制

1 2 3
1 1
1 1

输出

复制

4

思路:扩欧+换元

这个博客讲的是怎么求ax+by=c的解集:https://blog.csdn.net/qq547276542/article/details/49667283

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
ll a,b,c,p1,p2,q1,q2;
ll extgcd(ll a,ll b,ll &x,ll &y){
    ll d=a;
    if(b!=0){
        d=extgcd(b,a%b,y,x);
        y-=(a/b)*x;
    }
    else {
        x=1;
        y=0;
    }
    return d;
}
ll f(ll x,ll y){
    return p2*x*x+p1*x+q2*y*y+q1*y;
}
int main(){
    scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&p1,&p2,&q1,&q2);
    ll x1,x0,y1,y0;
    ll gcd=extgcd(a,b,x1,y1);
    if(c%gcd||!gcd){
        printf("Kuon\n");
        return 0;
    }
    ll ans;
    if(a&&!(c%a)&&!b)ans=f(c/a,0);
    else if(!a&&(b&&!(c%b)))ans=f(0,c/b);
    else{
         double A=q2+p2*b*b/(double)(a*a);
         double B=(double)q1-(p1*b*a+2*b*c*p2)/(double)(a*a);
         double C=(p2*c*c+p1*c*a)/(double)(a*a);
         double K=-B/(double)(2*A);
         y0=y1*c/gcd;
         ll ky=-a/gcd;
         int t=(K-y0)/(double)ky;
         ll y=y0+ky*t;
         ans=f((c-b*y)/a,y);
         for(int i=-3;i<=3;i++){
            y=y0+ky*(t+i);
            ans=min(ans,f((c-b*y)/a,y));
         }
    }
    printf("%lld\n",ans);
}

J - Princess Principal

题目描述

阿尔比恩王国(the Albion Kingdom)潜伏着一群代号“白鸽队(Team White Pigeon)”的间谍。在没有任务的时候,她们会进行各种各样的训练,比如快速判断一个文档有没有语法错误,这有助于她们鉴别写文档的人受教育程度。
这次用于训练的是一个含有n个括号的文档。括号一共有m种,每种括号都有左括号和右括号两种形式。我们定义用如下的方式定义一个合法的文档:
1.一个空的字符串是一个合法的文档。
2.如果A,B都是合法的文档,那么AB也是合法的文档。
3.如果S是合法的文档,那么aSb也是合法的文档,其中a,b是同一种括号,并且a是左括号,b是右括号。
现在给出q个询问,每次询问只考虑文档第l至r个字符的情况下,文档是不是合法的。

输入描述:

第一行两个整数n,m,q(1 ≤ n,m,q ≤ 106)。
第二行有n个空格隔开的整数x,第i个整数xi(0 ≤ xi < m*2)代表文档中的第i个字符是第种括号。另外,如果xi是偶数,它代表一个左括号,否则它代表一个右括号。
接下来q行,每行两个空格隔开的整数l,r(1 ≤ l ≤ r ≤ n),代表询问第l至r个字符构成的字符串是否是一个合法的文档。

输出描述:

输出共q行,如果询问的字符串是一个合法的文档,输出"Yes",否则输出"No"。

 

示例1

输入

复制

6 4 3
0 2 3 1 4 7
1 4
1 5
5 6

输出

复制

Yes
No
No

刚开始每次询问都进行进栈出栈操作,然后当然就TLE了

思路:

我们维护一个栈,若进栈的括号刚好和栈顶的括号匹配,那么栈顶元素出栈

eg、()或{([])}

反之就让其进栈,用L数组记录操作每个括号时,栈顶的编号(栈顶的编号用que数组来表示,top表示栈内元素个数)

eg{ ( ) } { [ }

L对应的值是:1,2,1,0,5,6,7

每次询问的区间,若是匹配的,那么L[l-1]==L[r],也就是说在[l,r]区间内没有元素是匹配前面的,并且,这个区间全抵消了,L[l-1]=L[r]

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
const int N=1e6+5;
int a[N],que[N],L[N];
int main(){
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int top=0;
    for(int i=1;i<=n;i++){
        if(!top)que[++top]=i;
        else{
            if(a[i]/2==a[que[top]]/2&&a[i]==a[que[top]]+1)top--;
            else que[++top]=i;
        }
        L[i]=que[top];
    }
    int l,r;
    while(q--){
        scanf("%d%d",&l,&r);
        if((r-l)&1){
            if(L[r]==L[l-1])printf("Yes\n");
            else printf("No\n");
        }
        else printf("No\n");
    }
}

L- New Game!

题目描述

Eagle Jump公司正在开发一款新的游戏。Hifumi Takimoto作为其中的员工,获得了提前试玩的机会。现在她正在试图通过一个迷宫。
这个迷宫有一些特点。为了方便描述,我们对这个迷宫建立平面直角坐标系。迷宫中有两条平行直线 L1:Ax+By+C1=0, L2:Ax+By+C2=0,还有 n 个圆 。角色在直线上、圆上、园内行走不消耗体力。在其他位置上由S点走到T点消耗的体力为S和T的欧几里得距离。
Hifumi Takimoto想从 L1 出发,走到 L2 。请计算最少需要多少体力。

输入描述:

第一行五个正整数 n,A,B,C1,C2 (1≤ n ≤ 1000, -10000 ≤ A,B,C1,C2 ≤ 10000),其中 A,B 不同时为 0。
接下来 n 行每行三个整数 x,y,r(-10000 ≤ x,y ≤ 10000, 1≤ r ≤ 10000) 表示一个圆心为 (x,y),半径为 r 的圆。

输出描述:

仅一行一个实数表示答案。与正确结果的绝对误差或者相对误差不超过 10-4 即算正确。

示例1

输入

复制

2 0 1 0 -4
0 1 1
1 3 1

输出

复制

0.236068

思路:

把两条线和中间的圆看成是点,然后spfa跑最短路

建边时:

两条线之间建边(平行直线的距离公式)

每两个圆之间建边(圆心之间距离减去两个圆半径)

每个圆和每条线建边(点到直线的距离公式减半径)

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const int N=1010;
int head[N],vis[N],cnt,n;
double d[N],x[N],y[N],r[N];
struct pro{
    int to,next;
    double cost;
};
pro edge[2*N*N];

void init(){
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=N;i++)d[i]=10e+10;
    cnt=0;
}

void addEdge(int from,int to,double cost){
    edge[cnt].to=to;
    edge[cnt].cost=cost;
    edge[cnt].next=head[from];
    head[from]=cnt++;
    edge[cnt].to=from;
    edge[cnt].cost=cost;
    edge[cnt].next=head[to];
    head[to]=cnt++;
}

void createMap(){
    double A,B,C1,C2,tmp,dist;
    scanf("%d%lf%lf%lf%lf",&n,&A,&B,&C1,&C2);
    tmp=fabs(C2-C1)/sqrt(A*A+B*B);
    d[n+1]=tmp;
    addEdge(0,n+1,tmp);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
        //连边0到点i,点i到n
        dist=fabs(A*x[i]+B*y[i]+C1)/sqrt(A*A+B*B)-r[i];
        tmp=max(0.0,dist);
        addEdge(0,i,tmp);
        dist=fabs(A*x[i]+B*y[i]+C2)/sqrt(A*A+B*B)-r[i];
        tmp=max(0.0,dist);
        addEdge(i,n+1,tmp);
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){//注意这一定是i+1,要不然就重复建边了
            //if(i==j)continue;
            //连边i到j
            dist=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
            tmp=max(0.0,dist-r[i]-r[j]);
            addEdge(i,j,tmp);
        }
    }
}

void spfa(){
    queue<int>q;
    q.push(0);
    d[0]=0;
    vis[0]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int p=head[u];p!=-1;p=edge[p].next){
            int v=edge[p].to;
            if(d[v]>d[u]+edge[p].cost){
                d[v]=d[u]+edge[p].cost;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

int main(){
    init();
    createMap();
    spfa();
    printf("%f\n",d[n+1]);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值