Day1
开个博客,一来作为总结,二来提醒自己还有很多内容需要不断完善。
A - 整数分解为2的幂(51Nod 1383)
课上学习了聚聚的代码,以及划分子问题的思想。
考虑数 n = 1+2+4+……,即1和其他2次幂,其他2次幂提取出2,又变为子问题求解。
#include <cstdio>
const int maxn = 1e6+10;
#define LL long long
const LL mod = 1e9+7;
LL a[maxn];
LL dfs(int n){
if(n <= 1) return 1;
if(a[n]) return a[n];
if(n & 1){
return a[n] = dfs(n-1)%mod;
}else{
return a[n] = (dfs(n-1)%mod + dfs(n/2)%mod) % mod;
}
}
int main(){
int n;
while(scanf("%d", &n) != EOF){
printf("%lld\n", dfs(n));
}
return 0;
}
B - Permutations(poj 2369)
Thinking:
看完题目,可以想到是置换群的知识。
置换:有限集合S到自身的一个一一映射叫做一个置换,即n个元的一种排列变为另一种排列。
题目里的Pn(k)可以看作置换的乘积。
题目思路是求置换中循环节长度,再求其最小公倍数。
最小公倍数求法是a*b/gcd(a,b)。
循环节长度此处用dfs()遍历求得。
#include <cstdio>
bool vis[1005];
int a[1005];
int num;
//求每个循环节的长度
void dfs(int n){
if(vis[n]) return;
vis[n] = true;
num++;
dfs(a[n]);
}
int gcd(int a, int b){
return b ? gcd(b, a%b) : a;
}
int lcm(int a, int b){ //最小公倍数
return a/gcd(a, b)*b;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++){ scanf("%d",&a[i]); }
int k = 1;
for(int i=1; i<=n; i++){
if(!vis[i]){
num=0;
dfs(i);
k = lcm(k, num);
}
}
printf("%d",k);
return 0;
}
C - Invoker
Thinking:
最开始对题意有了错误理解,(错误想法原因是看不懂题目所以瞎猜样例),错误思路:利用容斥原理,分An种情况,即Ai代表用i(1<=i<=m)种元素组成n个元素的技能,然后思考消去重复部分,然后就没有然后了。
下来我参考了ppt的课后提示,是标准的polya定理题。(这个定理现在还不是很懂)
参考了ppt中旋转和翻折置换的公式,不等价方案数的性质等概念还很模糊。
参考别人的代码勉强过了这题。
还有就是关于求组合数的两种方法:1、打表。 2、逆元求组合数。
还有就是关于逆元的求法: 1、扩展欧几里得算法。 2、费马小定理。
这个参考博客
#include <cstdio>
#define LL long long
const LL maxn(10010), mod(1e9+7);
LL pow(LL a, LL n, LL p){
LL ans = 1;
while(n){
if(n&1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
LL niyuan(LL a, LL b){
return pow(a, b-2, b);
}
LL gcd(LL a, LL b){
return b==0 ? a : gcd(b, a%b);
}
LL polya(LL m, LL n){
LL i, ans=0;
//旋转
for(i=1; i<=n; i++){
ans = (ans + pow(m, gcd(i,n),mod))%mod;
}
//翻转
if(n&1){
ans = (ans + n*pow(m, (n+1)/2, mod))%mod;
}else{
ans = (ans + n/2*(pow(m,n/2+1, mod) + pow(m, n/2, mod))%mod)%mod;
}
ans = ans%mod*niyuan(2*n,mod)%mod;
return ans;
}
int main(){
int cas;
scanf("%d",&cas);
for(int ca=1; ca<=cas; ca++){
int n,m;
scanf("%d%d",&m, &n);
printf("Case #%d: %lld\n",ca, polya(m,n));
}
return 0;
}
update(2018-08-31)
Burnside定理:如果某一个方案s经过一个置换f后不变,则称s为f的不动点,将f的所有不动点数目记为c(f),那么等价类数目就等于所有c(f)的平均值。
polya定理:如果将置换f分解成m(f)个循环的乘积,每个循环内所有格子的颜色必须相同。并假设涂k种颜色的话,那么c(f)等于k^m(f)。 得: 等价类的个数就是所有置换的k^m(f)的平均值。
本题是等价类计数问题,有两种置换:旋转和翻折。训练指南147页。
D - Aeroplane chess
想起课上那个饮料问题,求平均次数即求期望。与此题相似,找递推公式。
状态定义:dp[i]为扔筛子期望数。
看ppt提示说逆推,想了想,发现确实如此,因为正推时有多个起点且概率不等,枚举时无法计算。
逆推时分两种情况:1、飞行点和起点以及终点不用投筛子。 2、其他点则枚举筛子6个等概率点。
再扔一次筛子期望加一,因为枚举计算的是下一个状态的期望,当前状态则需要多仍一次才可以。
#include <cstdio>
#include <cstring>
const int N(100010);
int flight[N];
double dp[N];
int main(){
int n,m;
while( scanf("%d%d", &n, &m) && (n||m) ){
memset(flight, -1, sizeof(flight));
for(int i=0; i<m; i++){
int x,y;
scanf("%d%d",&x, &y);
flight[x] = y;
}
memset(dp, 0, sizeof(dp));
for(int i=n-1; i>=0; i--){
if(flight[i] != -1){
dp[i] = dp[flight[i]];
}else{
dp[i] = 1;
for(int j=i+1; j<=i+6; j++){
dp[i] += 1.0/6*dp[j];
}
}
}
printf("%.4lf\n", dp[0]);
}
return 0;
}
后面还有很多题,奈何水平有限,所以慢慢补吧。
作者:seaupnice
出处:https://www.cnblogs.com/seaupnice/p/9404652.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律