zoukankan      html  css  js  c++  java
  • 【洛谷】【洛谷月赛】4月月赛Round 1/2

    洛谷月赛“月”来“月”丧了,一月更比一月丧,做得我十分不“月”……

    4月的两轮月赛,都只会T1,就写一下吧,等待后续更新……

    先看看Round1的T1:

    【R1T1】

    网址:点我

    【题意简述】

    给定一个长度为n的序列,其中的元素均是1~m之间的正整数。

    要求从中选出k个数,交换它们的位置,其他未被选中的数保持不变,使得变换后的序列中,相等的数总是排在一段连续区间。

    要求最小化k。

    1<=n<=105,1<=m<=20

    【思路】

    ①想到枚举这n个数的全排列,对每个满足条件的全排列进行计算,更新答案。dfs的中途可以把已经不满足的递归树切掉,优化一下。时间复杂度O(n!*n),期望得分20。

    ②发现因为最后的序列一定是m个连续的相同段组成,考虑枚举m的全排列,这样可以保证答案合法,再统计。时间复杂度O(m!*n),期望得分40。

    ③对于②算法,可以用m个桶记录下原序列的前缀和,记sum[i][j]为序列1~i位中数j的个数,则一段区间[l,r]内不是j的个数为sum[r][j]-sum[l-1][j],把最后的统计优化到m。时间复杂度O(m!*m),期望得分70。

    ④考虑对③进行优化,发现有特殊的最优子结构性质。在③中,针对两个m的全排列,如果它们的前i位的数相同,但是可以有不同的顺序。

    例如:1,5,3,2,4和2,5,1,3,4,它们的前4位数相同,但是顺序不一定要相同。它们的前4位在原序列中的长度相同。对于第5位,没有必要枚举所有的前四位的全排列,只需要在数相同的全排列中寻找最小值即可。

    即对于一个k最优的解,它对应的m的全排列是P,P的前i位记作Pi。必然有Pi是所有Pi的全排列中的最优解。即最优子结构性质。

    考虑进行状压dp,用f[S]表示集合S的全排列对应到原序列的前面若干位中的最优解。S只可能包含1~m之间的正整数。

    则f[S]=min( f[S-k] + (sum[r(S)][k]-sum[r(S-k)][k]) ) k∈S。sum数组即③中sum数组,r(S)表示集合S(表示一个m的排列)对应到原序列中的长度。

    时间复杂度O(2m*m),期望得分100。

    【代码】

     1 #include<cstdio>
     2 #define F(i,a,b) for(int i=a;i<=b;++i)
     3 #define F2(i,a,b) for(int i=a;i<b;++i)
     4 int n,m,num[20],sum[100001][20],f[1<<20];
     5 inline int Max(int p,int q){return p>q?p:q;}
     6 void init(){
     7     int x;
     8     scanf("%d%d",&n,&m);
     9     F(i,1,n) scanf("%d",&x),sum[i][x-1]=1,++num[x-1];
    10     F2(j,0,m) F(i,1,n) sum[i][j]+=sum[i-1][j];
    11 }
    12 int main(){
    13     init();
    14     int s;
    15     F2(S,1,1<<m){
    16         s=0;
    17         F2(i,0,m)
    18             if((S>>i)&1)s+=num[i];
    19         F2(i,0,m)
    20             if((S>>i)&1)f[S]=Max(f[S],f[S^(1<<i)]+sum[s][i]-sum[s-num[i]][i]);
    21     }
    22     printf("%d",n-f[(1<<m)-1]);
    23     return 0;
    24 }

    我这里是f[S]表示最多的不动元素,会稍微好算一点点……原理不变。

    R1接下来的就不会了,都是大丧题。

    【R2T1】

    网址:点我

    【题意简述】

    给定n,对于x=1~n,求出 (sum_{i=1}^{n}x;mod;n) 。

    【思路】

    就直接讲吧,我先打出了这样一个表格:

    第 i 行第 j 列表示 i mod j 的值。

    从左往右竖着看,第1列是0,第2列重复1,0循环,第3列重复1,2,0循环……

    每加入一列,就给总结果加上了若干个等差数列。

    对于等差数列的加法,可以用两次差分,最后两次前缀和的方法把其变为常数时间。

    总共要进行 (sum_{i=1}^{n}frac{n}{i};=;n;ln(n)) 次差分。时间复杂度O(n*lnn)。

    【代码】

     1 #include<cstdio>
     2 long long n,a[1000002];
     3 int main(){
     4     scanf("%d",&n);
     5     for(int i=2;i<=n;++i){
     6         for(int j=0;j<=n;j+=i)
     7             a[j]-=i,a[j+1]+=i;
     8         ++a[0];
     9     }
    10     for(int i=1;i<=n;++i) a[i]=a[i-1]+a[i];
    11     a[0]=0;
    12     for(int i=1;i<=n;++i) a[i]=a[i-1]+a[i];
    13     for(int i=1;i<=n;++i) printf("%lld ",a[i]);
    14     return 0;
    15 }
  • 相关阅读:
    阈值处理——实例分析
    阈值处理
    split()函数+merge()函数
    imread函数+cvtColor()函数
    OpenCV3.2.0+VS2015开发环境配置
    Javascript中的async await
    React Native 系列(一)
    React Native 系列(三)
    React Native 系列(六)
    React Native 系列(七)
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/6730577.html
Copyright © 2011-2022 走看看