【题解】AtCoder Beginner Contest 239
A - Horizon
题面
给定一个 x ,输出 \(\sqrt{x(x+12800000)}\)
思路
精度要求过低,直接输出即可
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
typedef long long ll;
ll H;
int main(){
cin>>H;
printf("%.6lf\n",sqrt(H*(H+12800000)));
return 0;
}
B - Integer Division
题面
给定一个 x ,输出 \(\left \lfloor \frac{x}{10} \right \rfloor\)
思路
注意到是向下取整,所以判一判 x 是 10 的倍数的情况就好啦
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
typedef long long ll;
ll H;
int main(){
cin>>H;
if(!(H%10)) printf("%lld\n",H/10);
else printf("%lld\n",H/10+(-1)*(H<0));
return 0;
}
C - Knight Fork
题面
给你两个点的坐标,问是否存在一个点使得该点到两点的距离都为 \(\sqrt{5}\) ?
思路
考虑到距离为 \(\sqrt{5}\) 的两个点只可能是一个坐标轴上差 1 ,另一个差 2 ,直接枚举可能的点,再枚举距离另一个点为 \(\sqrt{5}\) 的点,判断两次枚举是否有重合即可。
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
typedef long long ll;
ll xx1,yy1,xx2,yy2;
ll ans[233][33],cnt,rere=0;
void doit(ll x,ll y){
ans[++cnt][1]=xx1+x;
ans[cnt][2]=yy1+y;
return;
}
bool findit(ll x,ll y){
for(ll i=1;i<=cnt;++i) if((xx2+x)==ans[i][1]&&(yy2+y)==ans[i][2]) return true;
return false;
}
int main(){
cin>>xx1>>yy1>>xx2>>yy2;
for(ll x=1;x<=2;++x){
for(ll fx=-1;fx<=1;++fx,++fx){
ll y=(x==1?2:1);
for(ll fy=-1;fy<=1;++fy,++fy)
doit(x*fx,y*fy);
}
}for(ll x=1;x<=2;++x){
for(ll fx=-1;fx<=1;++fx,++fx){
ll y=(x==1?2:1);
for(ll fy=-1;fy<=1;++fy,++fy)
rere+=findit(x*fx,y*fy);
}
}
if(rere) printf("Yes\n");
else printf("No\n");
return 0;
}
D - Prime Sum Game
题面
小T先在 \(A\sim B\) 中选择一个数,小A再在 \(C\sim D\) 中选择一个数,如果两个数之和为质数则小A赢,否则小T赢。
思路
无需和它死磕,范围很小,直接枚举小T的每种取值能不能赢即可。
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
typedef long long ll;
ll a,b,c,d;
bool ipr(ll fafa){
if(fafa==1) return false;
if(fafa==2||fafa==3||fafa==5||fafa==7||fafa==11) return true;
ll lim=sqrt(fafa)+1;
for(ll i=2;i<=lim;++i) if(fafa%i==0) return false;
return true;
}
bool takahashi(ll num){
for(ll i=c;i<=d;++i)
if(ipr(num+i)) return false;
return true;
}
int main(){
cin>>a>>b>>c>>d;
for(ll i=a;i<=b;++i){
if(takahashi(i)){
printf("Takahashi\n");
return 0;
}
}
printf("Aoki\n");
return 0;
}
E - Subtree K-th Max
题面
给定一棵树,点有点权,1 号点为根,每次询问以某个点为根的子树内第 k 大的点权是多少。
思路
k 很小,开数组记录并暴力合并子树答案即可。
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
typedef long long ll;
ll ans[233333][23];
ll n,q;
priority_queue<ll,vector<ll>,less<ll> > qqqqq;
struct edge{
ll nxt,to;
}ed[233333];
ll pnt[233333];
ll head[233333],cnt;ll aaa,bbb;
void addd(ll from,ll to){
ed[++cnt].to=to;
ed[cnt].nxt=head[from];
head[from]=cnt;
return;
}
void DFS(ll now,ll fa){
while(qqqqq.size()){
qqqqq.pop();
}
for(ll i=head[now];i;i=ed[i].nxt){
ll tt=ed[i].to;
if(tt==fa) continue;
DFS(tt,now);
}
for(ll i=head[now];i;i=ed[i].nxt){
ll tt=ed[i].to;
if(tt==fa) continue;
for(ll j=1;j<=20;++j){
if(!ans[tt][j]) break;
qqqqq.push(ans[tt][j]);
}
}
qqqqq.push(pnt[now]);
for(ll i=1;i<=20;++i){
if(qqqqq.size()){
ans[now][i]=qqqqq.top();
qqqqq.pop();
}
else break;
}
return;
}
int main(){
cin>>n>>q;
for(ll i=1;i<=n;++i){
cin>>aaa;
pnt[i]=aaa;
}
for(ll i=1;i<n;++i){
cin>>aaa>>bbb;
addd(aaa,bbb);
addd(bbb,aaa);
}
DFS(1,0);
for(ll i=1;i<=q;++i){
cin>>aaa>>bbb;
cout<<ans[aaa][bbb]<<endl;
}
return 0;
}
F - Construct Highway
题面
给你一个森林,问能不能加 \(n-m-1\) 条边把它变成每个点的度为 \(d_i\) 的树
思路
无解?
情况很多,但是我们现在只考虑一种,即所有点希望的度数之和不等于 \(2n-2\) (也就是最后的树的边数不是 \(n-1\))
原来没有边?
判无解,直接乱连不就行了?
很遗憾,不行,因为我们在乱连的时候再怎么乱也不会连出环来,但是代码可能,甚至可能是重边、自环。
怎么让它无环呢?
加并查集,再乱连不就行了?
还是不行,因为如果上来就连两个 还需要 的度为 1 的点,这两个点 遂与外人间隔 ,然后就寄了。
所以,我们需要按需要把点分成两类:大于 1 的和等于 1 的。
每次都把等于 1 的点连向大于 1 的点,再更新,然后就好了
正确性?
正确性是显然的,由于每次都从两个集合中各挑一个,所以至少有一个的需求为 1 ,也就是说,加边以后,至少会让一个点与外人间隔,后遂无问津者,所以不会有环;其次,我们只连两个集合之间的,不会出现连完就趋势的情况
这也反映出一个无解情况,即上来就存在与世隔绝的点一定是不行的。
回到这里,那我们这样,再乱连,就行了?
还是不行,我们思考一下,之所以不让他与外人间隔,是因为他还需要与别的点交互,但是在连完最后一条边以后,形成的连通块理应是封闭的。所以最后的最后应该是大于 1 的集合为空,而等于 1 的集合大小 正好为 2 ,否则GG
这样,只需要在这里判一下无解即可。
于是,就好了
再考虑原来有边的情况
我们要把它转化成若干个块块,使得他们两两之间没有边
满足条件的就是 连通块 ,直接并查集
然后,怎么使用呢?
只要将每个连通块中的代表元拎出来,就能将原图转化为原来没边的形式了。
那么,怎么再转换回去呢?
先明确一个东西,对于两个连通块,显然在他们两个中各任意选一个点连起来就行了,也就是说我们可以记录每个代表元拥有哪些边,再直接分配即可。
更简单地,连连通块的时候直接分配下去即可
然后就做完了
代码
//吾日八省吾身:
//输入多而不快读乎?
//题目标注而不freopen乎?
//乘除并列先乘后除乎?
//不手撕样例直接写代码乎?
//不仔细读题直接关页面乎?
//1e9而不开long long乎?
//Ctrl+V而不改名称乎?(papaw->papan IMPLIES tg1=->2=)
//相信评测神机乎?
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<utility>
#include<deque>
#include<ctime>
#include<sstream>
#include<list>
#include<bitset>
using namespace std;
const int MAXN(200233);
stack<int> ndd[MAXN];
queue<int> ones;
queue<int> zzzs;
int fa[MAXN];
int n,m,d[MAXN],a,b,aaaaa,bbbbb;
int ssd[MAXN];
int sumd;
int papaw[MAXN];
int eda[MAXN],edb[MAXN],cnt;
int getfa(int x){
if(x==fa[x]) return x;
else return fa[x]=getfa(fa[x]);
}
int R(){
int x=0,f=1;
char c='c';
while(c<'0'||c>'9'){
f=f*(c=='-'?-1:1);
c=getchar();
}
while(c<='9'&&c>='0'){
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return x*f;
}
int main(){
n=R();m=R();
for(int i=1;i<=n;++i){
d[i]=R();
fa[i]=i;
sumd+=d[i];
}
for(int i=1;i<=m;++i){
a=R();b=R();
aaaaa=getfa(a);
bbbbb=getfa(b);
fa[aaaaa]=bbbbb;
ssd[a]++,ssd[b]++;
if(ssd[a]>d[a]||ssd[b]>d[b]){
cout<<-1<<endl;
return 0;
}
}
if(sumd!=(n<<1)-2){
printf("-1\n");
return 0;
}
for(int i=1;i<=n;++i){
getfa(i);
papaw[fa[i]]+=(d[i]-ssd[i]);
for(int j=ssd[i];d[i]-j;++j) ndd[fa[i]].push(i);
}
for(int i=1;i<=n;++i){
if(papaw[i]>1) zzzs.push(i);
if(papaw[i]==1) ones.push(i);
if(i==fa[i]&&!papaw[i]){
cout<<-1<<endl;
return 0;
}
}
while(!ones.empty()){
if(zzzs.empty()) break;
aaaaa=ones.front(),bbbbb=zzzs.front();
ones.pop();
if(--papaw[bbbbb]==1){
zzzs.pop();
ones.push(bbbbb);
}
eda[++cnt]=ndd[aaaaa].top();
edb[cnt]=ndd[bbbbb].top();
ndd[aaaaa].pop();ndd[bbbbb].pop();
}
if(ones.size()!=2){
cout<<-1<<endl;
return 0;
}
aaaaa=ones.front();
ones.pop();
bbbbb=ones.front();
eda[++cnt]=ndd[aaaaa].top();
edb[cnt]=ndd[bbbbb].top();
for(int i=1;i<=cnt;++i)
printf("%d %d\n",eda[i],edb[i]);
return 0;
}