UOJ Easy Round #1 题解

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

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/109525117