2019 hdu 第四场补题 (1 ,签到题
因为太菜了,前几场的题都没补,从今天开始慢慢补
以后晚上要早睡,早上早起,抽出时间看书,每天只肝一局杀戮尖塔,上床不玩手机
每周五把一周总结的板子打印下来,毕竟老年人记性
补题顺序按难度升序
1001 AND Minimum Spanning Tree
题意:定义边的权值为两结点的&和,构造字典序最小的最小生成树
分析:
1.构造型算法
2.二进制数字默认有前导0,在纸上随便画画就发现构造将相邻点最低位0置为1,其余位为0为最优解,如果范围不够则直接置为1
题解:
1001. AND Minimum Spanning Tree
This is a very simple problem. If N is 2^k-1, the cost of MST is 1, otherwise it’s 0. Lexicographically smallest solution is also trivial. 2*k’s parent is 1, 4*k+1’s parent is 2, 8*k+3’s parent is 4, and so on. If N is 2^k-1, only N’s parent is not determined after this process. In this case, N’s parent is 1.
代码:
#include<algorithm> #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<string> #include<map> #include<queue> #include<stack> #include<list> #include<set> using namespace std; typedef long long ll; typedef pair<ll,ll> P; typedef long double ld; #define mem(x) memset(x, 0, sizeof(x)) #define me(x) memset(x, -1, sizeof(x)) #define fo(i,n) for(i=0; i<n; i++) #define sc(x) scanf("%I64d", &x) #define sca(n,m) scanf("%I64d%I64d", &n, &m) #define pr(x) printf("%I64d\n", x) #define pri(x) printf("%I64d ", x) #define lowbit(x) x&-x const ll MOD = 1e9 + 7; const ll oo = 1e18; const ll N = 4e5 + 5; ll vis[N], a[N]; int main() { ll i, j ,k; ll n, m, t, x; //cout<<(ll)pow(2,18)<<endl; cin>>t; while(t--) { cin>>n; mem(a); ll ans=0; for(i=2; i<=n; i++) { if(i%2) { k=0;x=i; while(1) { if(x%2==0) { //k++; break; } x>>=1; k++; } k=1<<k; if(k<=n) a[i]=k; else a[i]=1, ans++; } } cout<<ans<<endl; for(i=2; i<n; i++) { if(a[i]<=1) cout<<1<<" "; else cout<<a[i]<<" "; } if(a[n]<=1) cout<<1<<endl; else cout<<a[n]<<endl; } return 0; }
1007 Just an Old Puzzle
题意:15数码归位问题,给点初始状态,求120步之内是否可以归位
分析:
1.如果题目是求最短路径,可以用bfs或A*算法,但15个的状态太多也不会写
2.所以这道题其实很难
3.但是队友的书上正好有数码归位问题的结论(!):将数码排列一行,目标状态逆序对数目的奇偶性与初始状态一致则有解
4.由于每一步最多衍生出3种状态,3^120 >> 16! ,所以有解保证120步可达
跟题目一样迷的题解:
1007. Just an Old Puzzle
The solution consists of three steps.
//大概有三个步骤
At first you have to match the numbers 1, 2, 3 and 4.
//1.匹配1 .2 .3 .4
To match these numbers, we only need the positions of 1, 2, 3, 4 and empty grid.
//匹配这四个,需要知道1.2.3.4和空格的位置
So the total number of possible states are P_16^5=524160.
//这总共有16^5=524160种状态
You can use BFS or any algorithms to find it.
//这个范围内允许可以用BFS或其他算法得到其归位的最小步数
Next, you have to match the numbers 5, 6, 7 and 8.
//2.同理,匹配5.6.7.8
And at last, you have to match the numbers 9~15.
//3.匹配9~15
The total possible statuses are 8! = 40320.
//总共8! = 40320种状态
You can check if you could find the solution by checking the parity of inversion number of input permutation.
// ??突然跳跃)可以通过判断逆序对数目的奇偶性求解
You can easily prove that in first step, the maximum distance to target status are 46.
By using similar way, you can prove that you can match the grid in 120 moves.
//易证,第一步最多要46步,同理可证120步之内还原是ok的
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=16; int ar[N],a[N]; int lowb(int t) { return t & (-t) ; } void add(int i, int v) { for ( ; i < N; ar[i] += v, i += lowb(i)); } int sum(int i) { int s = 0; for ( ; i > 0; s += ar[i], i -= lowb(i)); return s; } int main() { int n,m,T; scanf("%d",&T); while(T--) { n=4,m=4; int x,y,t,s=0,nu=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&t); if(t==0) x=i,y=j; else a[nu++]=t; } memset(ar,0,sizeof(ar)); for(int i=nu-1;i>=0;i--) { s+=sum(a[i]-1); add(a[i],1); } if(m&1) if(s&1)puts("No"); else puts("Yes"); else if(((n-x)^s)&1) puts("No"); else puts("Yes"); } return 0; }
题意:给定T组数据(T<=50000)n(n<=1e18),求将n唯一分解后质因子的最低指数;
分析:
1. 数学,贪心
2. 分析质因子的数量级: 1e18 = 1e9*1e9 ,若1e9之内没有质因子 ,则剩下的指数一定为1,(否则就超过1e18了),1e9太大会超时
3.继续分析:1e18 = 1e6*1e6*1e6 = 1e7*1e7*(1e4) = 1e8*1e8*(1e2) ,若1e6之内没有质因子 ,则剩下的指数一定为1或2,遍历1e6后若未完全分解根据剩下的是不是完全平方数判断结果(只能是1或2),T太大会超时
4.继续分析:1e18 = 1e4*1e4*1e4*1e4*1e2 = 1e5*1e5*1e5*1e3 = (p1:1e4*1e4)*(p2:1e4*1e4) = (p1:1e5*1e5)*(p2:1e5) ,若1e6之内没有质因子 ,则剩下的指数一定为1或2,3,4, 分别判断开根后是否为整数,有两个质因子的情况最低指数只能是1或2,判断是否能完全平方即可,比赛时感觉两个质因子的情况太复杂就没继续分析下去,结果发现因为是求最低指数所以多个大质因子结果只能是1或2反而判起来很简单orz,人蠢不能复聪
5.开方由于数字特别大要用powl()计算减少误差,而且两个参数必须强制转换成long double(常数也要,因为默认是double),也可以二分求开方结果 ,由于精度误差一般都在 +1~-1之内,也可以直接把结果p,p-1,p+1都判断是不是完全开方因子
题解:
1010. Minimal Power of Prime
Let's first factorize N using prime numbers not larger than N1/5. And let's denote M as the left part, all the prime factors of M are larger than N1/5. If M=1 then we are done, otherwise M can only be P2, P3, P4 or P2*Q2, here P and Q are prime numbers.
(1) If M1/4 is an integer, we can know that M=P4. Update answer using 4 and return.
(2) If M1/3 is an integer, we can know that M=P3. Update answer using 3 and return.
(3) If M1/2 is an integer, we can know that M=P2 or M=P2*Q2. No matter which situation, we can always update answer using 2 and return.
(4) If (1)(2)(3) are false, we can know that answer=1.
Since there are just O(N1/5/log(N)) prime numbers, so the expected running time is O(T*N1/5/log(N)).
代码:
#include<bits/stdc++.h> #define fi first #define se second #define rep( i ,x ,y ) for( int i= x; i<= y ;i++ ) #define reb( i ,y ,x ) for( int i= y; i>= x ;i-- ) #define mem( a ,x ) memset( a ,x ,sizeof(a)) using namespace std; typedef long long ll; typedef long double ld; typedef pair<int ,int> pii; typedef pair<ll ,ll> pll; typedef pair<string ,int> psi; const int inf = 0x3f3f3f3f; const int N = 10005; const int M = 200050; int T ,ans ,tot=0; int unprime[N]; ll n ,prime[N]; void euler( ){ unprime[1] = 1; rep( i ,1 ,N ){ if( !unprime[i] )prime[++tot] = i; rep( j ,1 ,tot ){ if( i*prime[j] >N )break; unprime[ i*prime[j] ] = 1; if( i%prime[j] == 0 )break; } } } int main( ){ scanf("%d" ,&T ); euler( ); //cout<<tot<<endl; while( T-- ){ ans = inf; scanf( "%lld" ,&n ); //cout<<n<<endl; rep( i ,1 ,tot ){ if( n%prime[i]==0 ){ int tmp = 0; while( n%prime[i] ==0 ){ n /= prime[i]; tmp++; } ans = min( ans ,tmp ); } if( n==1 )break; } if( n >= N ){ ll p1 ,p2 ,p3 ; ll p11 ,p22 ,p33; ll pp1 ,pp2 ,pp3; ld s = n; p1 = powl( s ,(ld)1.0/4.0 ); p11 = p1+1; pp1 = p1-1; p2 = powl( s ,(ld)1.0/3.0 ); p22 = p2+1; pp2 = p2-1; p3 = sqrt( (ld)s ); p33 = p3+1; pp3 = p3-1; //cout<<p1<<" "<<p2<<" "<<p3<<endl; //cout<<ans<<endl; if( n == p1*p1*p1*p1 || n == p11*p11*p11*p11 || n == pp1*pp1*pp1*pp1 ){ ans = min(ans ,4 );} else if( n == p2*p2*p2 || n == p22*p22*p22 || n == pp2*pp2*pp2 ){ ans = min( ans ,3 );} else if( n == p3*p3 || n == p33*p33 || n == pp3*pp3 ){ ans = min( ans ,2 );} else ans = min( ans ,1 ); } printf("%d\n" ,ans ); } return 0; }