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