Codeforces Round #624 (Div. 3)
D. Three Integers
题意:t(100)组数据,每组给三个数字a<=b<=c(1e4),每次可以把其中一个数字+1或者-1,问最少多少次操作可以使得c%b==0,b%a==0,并且输出操作后的abc。
思路:暴力枚举a,再暴力枚举a的倍数b,再暴力枚举b的倍数c,复杂度不到nlog^2,其实就是1+1/2+1/3...+1/n<lnn这个式子。需要注意abc最终结果并不一定还在1e4以内,比如,137 10000 10000,于是我选择了一个比较高的上界枚举,我直接取了2e4。首先a没必要取2e4,取1更好,b是a的倍数最差取得和a一样,一定也比2e4更小,c也同理。事实上c可以直接用c/b和c/b+b中的一个值作为答案即可,这样还可以省掉一个log。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int a,b,c; 18 int T; 19 int main(){ 20 rd(T); 21 while(T--){ 22 rd(a),rd(b),rd(c);int ans=0x7fffffff,ansa,ansb,ansc; 23 for(int i=1;i<=20000;i++) 24 for(int j=i;j<=20000;j+=i) 25 for(int k=j;k<=20000;k+=j){ 26 int tmp=abs(i-a)+abs(j-b)+abs(k-c); 27 if(tmp < ans){ans=tmp;ansa=i,ansb=j,ansc=k;} 28 } 29 printf("%d\n%d %d %d\n",ans,ansa,ansb,ansc); 30 } 31 return 0; 32 } 33 /**/
反思:这题需要注意两个点,首先应当注意到,对于1~n的每一个数枚举他们的因数总复杂度是nlog的,需要在nlog的时间和空间将它们预处理出来,再对因数枚举因数复杂度多一个log并且达不到这个log,预处理复杂度不变。通过c优化掉一个log的思路也需要掌握,可以理解为"只剩下一个数在枚举时不妨看看它是否完全可以不枚举而计算出最优答案"。
E. Construct the Binary Tree
题意:构造一个n(5000)个点的二叉树使得所有点深度之和为d(5000),输出是否可以,可以的话输出每个节点的父亲。
思路:n个节点的二叉树所有节点深度之和存在一个上界和下界,即一条链或者是尽量使其为满二叉树,也就是一层填满再填下一层。如果d不在这个范围内那么必然不可能。我们可以发现,可以通过移动某个节点使得当前深度总和+1或者-1。先考虑使得当前深度+1,我们找到两个点,他们深度相同并且尽可能深(我用了sort,很蠢,完全可以在n的时间解决),其中必然有一个点没有儿子而另一个点至多有一个儿子,我们只需要把没儿子的移到另一个上面即可。再考虑使得当前深度-1,找到最浅的,没有儿子且未被标记的节点,看其能否在深度-2的那一层找到可以当自己父亲的节点,有的话就直接移过去,没有的话就标记上,因为后面怎么移动他也不可能再在那一层找到可以成为其父亲的节点了(之所以从浅到深,是因为浅的改变会影响深的,而深的改变却不会影响浅的)。有了使其深度+1或者-1的方法,我们就可以从上界或者下界开始枚举即可。然而从下界枚举会更优秀,上界的话是从n^2级别开始枚举的,虽然合法的n并不能到5000级别,但复杂度绝对不如从下界枚举更优秀。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=5e3+10; 17 using namespace std; 18 int T,n,d; 19 struct node{ 20 int p,dep,id,siz; 21 bool operator < (const node o)const{ 22 return dep < o.dep || (dep == o.dep && siz > o.siz); 23 } 24 }a[N]; 25 bool cmp(node a,node b){return a.id < b.id;} 26 int hs[N]; 27 int main(){ 28 freopen("in.txt","r",stdin); 29 rd(T); 30 while(T--){ 31 rd(n);rd(d);int ans=0; 32 memset(a,0,sizeof(a));a[0].dep=-1; 33 for(int i=1;i<=n;i++)a[i].p=i/2,a[i].dep=a[i/2].dep+1,ans+=a[i].dep,a[i].id=i,a[i/2].siz++; 34 if(ans > d || (d > n*(n-1)/2))printf("NO\n"); 35 else { 36 while(ans < d){ 37 sort(a+1,a+n+1); 38 for(int i=1;i<=n;i++)hs[a[i].id]=i; 39 for(int i=n;i>1;i--){ 40 if(a[i].dep == a[i-1].dep){ 41 a[hs[a[i].p]].siz--; 42 a[i].p=a[i-1].id;a[i].dep++; 43 a[i-1].siz++; 44 ans++;break; 45 } 46 } 47 } 48 printf("YES\n");sort(a+1,a+n+1,cmp); 49 for(int i=2;i<=n;i++)printf("%d ",a[i].p);puts(""); 50 } 51 } 52 return 0; 53 } 54 /**/
反思:树的dep和与size和其实是一个东西(做题过程想到的)。