51nod 1421 最大取模值 奇怪的数学方法

这题确实经典.我旁边的一些神仙都用奇怪的方法A掉了,而我只能选择了贺题.

题目描述

给出一个数组a,求当a[i]>=a[j]时a[i]%a[j]最大的值.n<=10^5,a[i]<=10^6.

瞎搞过程

首先想到的当然是 n2 n 2 的暴力.把暴力打上去有 40 40 分.
然后我们考虑一下,对于 a[i] a [ i ] 最大的取模结果必然是小于 a[i] a [ i ] 的.
我们从大到小枚举被取模数,当 a[i]ans a [ i ] ≤ a n s 直接跳出循环.

#include<bits/stdc++.h>
/*省略快读快写*/
}using namespace chtholly;
using namespace std;
const int yuzu=2e5;
int a[yuzu|10];

int main(){
int i,j,n=read(),llx=0;
for (i=1;i<=n;++i) a[i]=read();
sort(a+1,a+n+1);
for (i=n;i&&a[i]>llx;--i){
  for (j=i+1;j<=n;++j){
    llx=max(llx,a[j]%a[i]);
    }
  }write(llx);
}

这样从40分变成60分.加了卡常头文件之后分数并没有变化.
那么只能考虑打复杂度正确的算法了.
一开始我想二分,但是挂了,后来我发现答案并没有可二分性.
好吧,我放弃了.
那么接下来我们看一个标算.

/*
我们来看,对于某个被取模数x来讲,能够得到的最大的模数是小于x的某一个倍数的最大的那个数取模x的值.
可以看到数组是可以排序,去重的.
那么我们可以处理出一个2*数据范围的数组d,d[i]表示a数组中小于i的最大数字.
那么对于每一个去重后的a[i],我们都可以枚举它的每一个倍数a[i]*j,用d[a[i]*j]去模a[i],取最大值即可.
*/
#include<bits/stdc++.h>
/*快读快写省略*/
}using namespace chtholly;
using namespace std;
const int yuzu=2e5,mulu=2e6;
int a[yuzu|10],d[mulu|10],n;
map<int,int> mp;

int main(){
int i,j,k=read(),llx=0;
for (int x;k--;) if (!d[x=read()]++) a[++n]=x;
memset(d,0,sizeof d);
sort(a+1,a+n+1,greater<int>());
for (j=1,i=mulu;i;--i){
  for (;j<=n&&a[j]>=i;) ++j;
  d[i]=a[j];
  }
for (i=1;i<=n;++i){
  for (j=2;j*a[i]<=mulu;++j){
    llx=max(llx,d[a[i]*j]%a[i]);
    } 
  }write(llx);
}

接下来是神犇styx的二分算法.我没看懂.
可惜这个代码无论如何总有这么一两个点 tle t l e ,我们都没救过来.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int vis[4000010],sum[4000010],k,n,a[200010],cnt;

int check(int x)
{
    for(register int i=1;i<=cnt;++i)
    {
        for(register int j=a[i];j<=1e6;j+=a[i])
        {
            if(sum[j+a[i]-1]-sum[j+x-1]>0)
            {
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    scanf("%d",&n);
    int tmp;
    for(register int i=1;i<=n;++i)
    {
        scanf("%d",&tmp);
        if(!vis[tmp])
        {
            vis[tmp]=1;
        }
    }
    for(register int i=1;i<=2e6;++i)
    {
        sum[i]=sum[i-1]+vis[i];
    }
    for(int i=1;i<=1e6;i++)
    {
        if(vis[i])
        {
            a[++cnt]=i;
        }
    }
    register int l=0,r=2e6,mid;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid))
        {
            l=mid;
        }
        else
        {
            r=mid-1;
        }
        if(r-l<=1)
        {
            r=check(r)?r:l;
            break;
        }
    }
    printf("%d\n",r);
}

然后是神仙yzy的set瞎搞算法.反正我还是不懂.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define N 200100
using namespace std;

int n,num[N],t,ans;
P tmp;
set<P>se;

int main()
{
    int i,j;
    cin>>n;
    for(i=1; i<=n; i++)
    {
        scanf("%d",&num[i]);
    }
    sort(num+1,num+n+1);
    se.insert(mp(num[2]/num[1]*num[1],num[1]));
    for(i=2; i<=n; i++)
    {
        for(;;)
        {
            tmp=(*se.begin());
            if(num[i]/tmp.se != tmp.fi/tmp.se)
            {
                se.erase(tmp);
                se.insert(mp(num[i]/tmp.se*tmp.se,tmp.se));
            }
            else
            {
                ans=max(ans,num[i]%tmp.se);
                break;
            }
        }
        se.insert(mp(num[i+1]/num[i]*num[i],num[i]));
    }
    cout<<ans;
}

好,谢谢大家.

题目 51nod 3478 涉及一个矩阵问题,要求通过最少的操作次数,使得矩阵中至少有 `RowCount` 行和 `ColumnCount` 列是回文的。解决这个问题的关键在于如何高效地枚举所有可能的行和列组合,并计算每种组合所需的操作次数。 ### 解法思路 1. **预处理每一行和每一列变为回文所需的最少操作次数**: - 对于每一行,计算将其变为回文所需的最少操作次数。这可以通过比较每对对称位置的是否相同来完成。 - 对于每一列,计算将其变为回文所需的最少操作次数,方法同上。 2. **枚举所有可能的行和列组合**: - 由于 `N` 和 `M` 的最大值为 8,因此可以枚举所有可能的行组合和列组合。 - 对于每一种组合,计算其所需的最少操作次数,并最小。 3. **计算操作次数**: - 对于每一种组合,需要计算哪些行和列需要修改,并且注意行和列的交叉点可能会重复计算,因此需要去重。 ### 代码实现 以下是一个可能的实现方式,使用了枚举和位运算来处理组合问题: ```python def min_operations_to_palindrome(matrix, row_count, col_count): import itertools N = len(matrix) M = len(matrix[0]) # Precompute the cost to make each row a palindrome row_cost = [] for i in range(N): cost = 0 for j in range(M // 2): if matrix[i][j] != matrix[i][M - 1 - j]: cost += 1 row_cost.append(cost) # Precompute the cost to make each column a palindrome col_cost = [] for j in range(M): cost = 0 for i in range(N // 2): if matrix[i][j] != matrix[N - 1 - i][j]: cost += 1 col_cost.append(cost) min_total_cost = float('inf') # Enumerate all combinations of rows and columns rows = list(range(N)) cols = list(range(M)) from itertools import combinations for row_comb in combinations(rows, row_count): for col_comb in combinations(cols, col_count): # Calculate the cost for this combination cost = 0 # Add row costs for r in row_comb: cost += row_cost[r] # Add column costs for c in col_comb: cost += col_cost[c] # Subtract the overlapping cells for r in row_comb: for c in col_comb: # Check if this cell is part of the palindrome calculation if r < N // 2 and c < M // 2: if matrix[r][c] != matrix[r][M - 1 - c] and matrix[N - 1 - r][c] != matrix[N - 1 - r][M - 1 - c]: cost -= 1 min_total_cost = min(min_total_cost, cost) return min_total_cost # Example usage matrix = [ [0, 1, 0], [1, 0, 1], [0, 1, 0] ] row_count = 2 col_count = 2 result = min_operations_to_palindrome(matrix, row_count, col_count) print(result) ``` ### 代码说明 - **预处理成本**:首先计算每一行和每一列变为回文所需的最少操作次数。 - **枚举组合**:使用 `itertools.combinations` 枚举所有可能的行和列组合。 - **计算成本**:对于每一种组合,计算其成本,并考虑行和列交叉点的重复计算问题。 ### 复杂度分析 - **时间复杂度**:由于 `N` 和 `M` 的最大值为 8,因此枚举所有组合的时间复杂度为 $ O(N^{RowCount} \times M^{ColCount}) $,这在实际中是可接受的。 - **空间复杂度**:主要是存储预处理的成本,空间复杂度为 $ O(N + M) $。 ### 相关问题 1. 如何优化矩阵中行和列的枚举组合以减少计算时间? 2. 在计算行和列的交叉点时,如何更高效地处理重复计算的问题? 3. 如果矩阵的大小增加到更大的范围,如何调整算法以保持效率? 4. 如何处理矩阵中行和列的回文条件不同时的情况? 5. 如何扩展算法以支持更多的操作类型,例如翻转某个区域的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值