清北学堂 2017-10-06

****

因为是刚听完课所以想把思路记下来,有一些其实也是一知半解的,如果有dalao可以帮忙讲解那真是再感谢不过了。

*****还有为什么我画图这么丑,哇的一下哭出声*******

Problem A. 最佳进制

如今我们最常用的是十进制,据说这是因为人有十根手指。
但事实上这并不是十分方便,10 只有四个因子 1、2、5、10,

像 1/3、1/6 这些分数在十进制下的十数表示就
不是很优美。在这种要求下,12、24 甚至60 进制会更好一些。
现在想求出不超过 n 的最佳进制数,也就是拥有最多的因子。
Input
第一包含一个整数 n,(1 ≤ n ≤ 1e16)。
Output
输出一个数 c,表示最佳进制数。

Examples
Input

 100

Output
60

Subtasks
对于20% 的数据,n<=100。
对于40% 的数据,n<=1e5。
对于60% 的数据,n<=1e9。
对于100% 的数据,k<=1e6。

注:若有多个解,输出最小的那个

解析:

求因子最多的数

一个大多数人都知道但我不知道的关系:x=∏Pi^ai //∏为累乘符号

即x为多个质因数(Pi)的ai次方的乘积

则这个数所有的因子个数ans=∏(ai+1)

例:12=(2^2)*(3^1)

还有就是如果ai是x的因子,则a1~a(i-1)一定都为x的因子(这里不是很能理解,如果这个数是质数咋办???求告知) 即a1>=a2>=a3….>=an

由于题目范围为1e16,且2*3*5*……*47>1e16,则分解因数只需到47

另外就是如果x|y,则x的每个质因数一定也为y的质因数,且幂次一定比在y的幂次小

又因为(2^3)*(3^2)与(2^2)*(3^3)的因子数是一样的,因此最好让小的质因子的幂次更高

下面就是愉快的代码时间了

#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
using namespace std;
long long n, maxd, ans;
int prime[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};
void dfs(int x, long long sum, long long nowd, long long Limit) {
	if (nowd > maxd || (nowd == maxd && sum < ans))
		maxd = nowd, ans = sum;
	if (x > 11) return;
	for (int i = 1; sum * prime[x] <= n && i <= Limit; i++)
		dfs(x + 1, sum *= prime[x], nowd * (i + 1), i);
}
int main()
{
	freopen("divisors.in","r",stdin);
	freopen("divisors.out","w",stdout);
	cin >> n;
	ans = 1;
	if (n > 1) dfs(1, 1, 1, 1e9);
	cout << ans << endl;
	return 0;  
}
     
     
    
    
   
   
  
  
 

 

Problem B. 树
File: tree.*
Time limit: 1s
Memory limit: 256MB
给出一棵节点数为n 的有根树,现在需要给每个节点标号,要求对应子树同构的节点标号相同。
若图G1,G2 同构,则存在G1 点集和G2 点集之间的双射ϕ,满足u ->v 是G1 中的一条边当且仅当
ϕ(u) ! ϕ(v) 是G2 中的一条边。
Input
第一包含一个数n(1<=n<=105),表示树的点数。
第二行包含n - 1 个数,第i 个数表示编号为i + 1 点的父节点编号,满足父节点的编号小于子节点,根
节点编号为1。
Output
一行包含n 个数,表示节点标号,标号大小范围在1 到n 之间。如果有多种标号,给出字典序最小的标
号。

Examples
Input
9
1 2 2 1 5 5 7 7

Output

1 2 3 3 4 3 2 3 3

Subtasks
对于30% 的数据,n<=12。
对于50% 的数据,n<=100。
对于100% 的数据,n<=105。

解析:

一道”简单的”Hash题

先说一下啥叫同构:即子树的节点和构成相同(即相对位置相同)

在下图中,4/5/7/8/9全为叶子结点,则他们位置相同;2/6都各有两个子节点,则它们位置相同;而1/3的子节点各不相同,应处于不同的位置(位置也即最后要求的节点编号)

 

另外,子树是可以旋转的,也就是说下面的这两棵树也是同构的

 

下面就开始Hash了

由于是否同构看到是子树,因此我们从叶子到根Hash,Hash值相同的则为同构

 

在上图中,令4/7/8/9/10/11都为空集,也就是[ ],则5/6均为[1,1],2/3为[1,2]/[2,1](一样的),1为[3,3].

Hash完可以发现4/7/8/9/10/11的值是一样的,5/6的值一样,2/3的值一样,1自己一个值

具体如何Hash呢?即将每个数看成n进制的,然后将它们化为10进制,例n进制下的12为10进制下的1*n+2

当所有节点的Hash值都求完后,我们设一个计数器cnt,每当出现一个新数,就cnt++,然后把cnt作为标号赋给当前值,若此Hash值之前已经出现,则直接输出

具体实现可用map函数,怎么写在代码里就不多说了

 

#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
        #include 
        using namespace std; const int N = 120000; vector 
        
          E[N]; map 
          
          
            , int> id; int ans[N], p[N], vis[N]; int n, cnt; int work(int x) { vector 
           
             u; for(int i = 0; i < E[x].size(); i++) { int y = E[x][i]; u.push_back(work(y)); } sort(u.begin(), u.end()); if (!id[u]) id[u] = ++cnt; ans[x] = id[u]; return ans[x];//返回x的hash值 } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); scanf("%d", &n); for(int i = 2; i <= n; i++) { scanf("%d", &p[i]); E[p[i]].push_back(i); } work(1); cnt = 0; for(int i = 1; i <= n; i++) { if (!vis[ans[i]]) { vis[ans[i]] = ++cnt; } ans[i] = vis[ans[i]]; } for(int i = 1; i <= n; i++) printf("%d ", ans[i]); puts(""); } 
            
           
          
         
      
     
     
    
    
   
   
  
  


Problem C. 关系
File: relations.*
Time limit: 1s
Memory limit: 256MB
有一个二元关系R,R 可以被表示成n*n 的布尔数组。
现在希望找到长度都为n 的数组f 和g,要求满足Rx,y = 1 当且仅当f(x)<=g(y)。
Input
第一行包含一个整数n(1<=n<=1000),表示数组的大小。
接下来的n 行表示二元关系R。
Output
第一行输出能否找到数组f 和g,如果能找到输出YES,否则输出NO。-1e9<=fi; gi<=109
第二行输出n 个整数,表示数组f。
第三行输出n 个整数,表示数组g 。

Examples
Input
3
111
110
100

Output

YES
0 1 2
2 1 0

Subtasks
对于20% 的数据,n<=10。
对于50% 的数据,n<=100。
对于100% 的数据,n<=1000。

解析:

Zcc dalao说看到这个就应该想到图啊(我没想到但我记住了)

如果a<=b,b<=c,则一定有a<=c

即R[a][b]=1,R[b][c]=1--->>R[a][c]=1若不满足此条件则输出”NO”

我们另R[a][b]=1当且仅当f[a]>g[b]时(为了建图方便)

如果R[x][y]=1,则连一条x到y+n(为了方便输出)

同理,如果R[x][y]=0,则连一条x到y+n

则可得到一个DAG(有向无环图)[因为a<b,b<c,c<a不可能同时成立,则应为DAG]

之后就拓扑排序求值啦(第几个拿到则为几)

代码

#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
        #include 
       
         using namespace std; int f[1005], g[1005], cnt[1005], n; int cmp(int i, int j) { if (cnt[i] == cnt[j]) return (i < j); return cnt[i] < cnt[j]; } int main() { string s[1005]; vector 
        
          v; freopen("relations.in", "r", stdin); freopen("relations.out", "w", stdout); cin >> n; for(int i=0; i 
         
           > s[i]; v.push_back(i); cnt[i] = 0; for(int j=0; j 
          
            s[y][j]) { correct = false; break; } if(!correct) break; } for(int i=0; i 
            
           
          
         
        
      
     
     
    
    
   
   
  
  
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值