A
由于 a , b a,b a,b 都是 g g g 的倍数,于是可以写成 a = g x , b = g y a=gx,b=gy a=gx,b=gy。
推一推:
a b = g l x y g 2 = g l x y = l g \begin{aligned} ab&=gl\\ xyg^2&=gl\\ xy&=\frac l g \end{aligned} abxyg2xy=gl=gl=gl
当 x = l g , y = l g x=\sqrt{\dfrac l g},y=\sqrt{\dfrac l g} x=gl,y=gl 时, x + y x+y x+y 有最小值,此时 a = b = l g = n a=b=\sqrt{lg}=\sqrt n a=b=lg=n,即最小值为 2 n 2\sqrt n 2n。
当 x = 1 , y = l g x=1,y=\dfrac l g x=1,y=gl 时, x + y x+y x+y 有最大值,此时 a = g , b = l a=g,b=l a=g,b=l,即最大值为 g + l g+l g+l。
要注意用long double来开根,不然精度不够。
代码如下:
#include <cstdio>
#include <cmath>
#define ll long long
ll T,g,l;
int main()
{
scanf("%lld",&T);while(T--)
{
scanf("%lld %lld",&g,&l);
ll a=(ll)floor(sqrt(1.0*g)*sqrt(1.0*l)+1e-10);
printf("%lld %lld\n",a+a,g+l);
}
}
B
模拟即可。对于每个文件夹,记录一个 map \text{map} map, map [ x ] \text{map}[x] map[x] 表示这个文件夹的路径后再加上 x x x 指向哪个文件夹,如果这个 x x x 直接用string会T一个点,可以将其哈希一下变成一个数值,这样就会快一些。
代码如下:
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 300010
int n,m,id=0;
struct node{
int last,to;string from;
node():last(0),to(-1){
from.clear();}
map<string,int> ne;
}a[maxn];
char s[maxn];
vector<string> d;
vector<string> split(char *S,int len){
vector<string> re;re.clear();
for(int i=1;i<len;i++){
int j=i;while(j+1<len&&S[j+1]>='a'&&S[j+1]<='z')j++;
string p(S,i,j-i+1);
re.push_back(p);
i=j+1;
}
return re;
}
int go(){
d=split(s,strlen(s));
int now=0;for(string j:d){
if(!a[now].ne.count(j)){
a[now].ne[j]=++id;
a[id].last=now;
a[id].from=j;
}
now=a[now].ne[j];
if(a[now].to!=-1)now=a[now].to;
}
return now;
}
vector<int> sta;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s);
int now=go();
scanf("%s",s);
int now2=go();
a[now].to=now2;
}
for(int i=1;i<=m;i++){
scanf("%s",s);
int p=go();sta.clear();
for(;p;p=a[p].last)sta.push_back(p);
if(sta.size()){
reverse(sta.begin(),sta.end());
for(int j:sta)cout<<"/"<<a[j].from;
cout<<"\n";
}else printf("/\n");
}
}
C
注意到边权是递增的,所以每条新边可以直接合并到并查集里面来维护最小生成树。
撤销的话写一手可撤销并查集即可,具体来说就是将并查集按秩合并,用栈记录下合并前的信息,每次撤销时取出栈顶信息即可,注意这里的并查集不能用路径压缩。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 500010
#define cn getchar
void read(int &x){
x=0;int f1=1;char ch=cn();
while(ch<'0'||ch>'9'){
if(ch=='-')f1=-1;ch=cn();}
while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn(); x*=f1;
}
void write(int x){
static char op[110];
static int t;t=0;
if(!x)putchar('0');
while(x)op[++t]=x%10+'0',x/=10;
while(t)putchar(op[t--]);
}
int n,m;
int fa[maxn],sz[maxn];
int cnt[maxn],sta[maxn],t=0;long long ans[maxn];
int findfa(int x){
return x==fa[x]?x:findfa(fa[x]);}
void add(int x,int y,int val){
x=findfa(x);y=findfa(y);
t++;cnt[t]=cnt[t-1];ans[t]=ans[t-1];
if(x==y)sta[t]=0;
else{
if(sz[x]<sz[y])swap(x,y);
sta[t]=y;fa[y]=x;sz[x]+=sz[y];
cnt[t]++;ans[t]+=val;
}
}
void del(int k){
while(k--){
int x=sta[t--];
sz[fa[x]]-=sz[x];fa[x]=x;
}
}
void print(int x){
printf("%lld\n",cnt[x]==n-1?ans[x]:0ll);}
int main()
{
read(n);read(m);
for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1;
for(int i=1,last=0,k;i<=m;i++){
char s[10];int x,y;
scanf("%s",s);
if(s[0]=='A'){
read(x);read(y);
if(last==2)del(k);
add(x,y,i);last=1;
print(t);
}else if(s[0]=='D'){
read(x);
if(last==2)del(k);
last=2,k=x;
print(t-x);
}else{
if(last==1)del(1);
last=0;print(t);
}
}
}