6.29总结

Posted leason-lyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了6.29总结相关的知识,希望对你有一定的参考价值。

6.29总结

比赛总结

得分

估分:0+30+40=70

实际:0+100+0=100

Rank 11

神奇,T2暴力切了!

woc,T2暴力就是正解!

第三题空超爆零


T1

什么鬼,完全不会嘛。。。弃掉!

正解:

树形dp/贪心(不会)

1、将环上的任意一条边删去,就成了一棵特殊的树——也即一条链;
2、这题根本不需要考虑环的构造方案,即不需要考虑点和点之间的连接方法(因为要求的只
是最小代价)。
这使我们容易想到一种可行的构造方法:先将树转换一条链,最后再加一条连接链的首位的
边,从而转换成环。

By 《树环转换》题解——约朋

设f[i][0/1]表示把以i为根的子树变为一条链的最小代价。
\[ f[i][0]=min\begincases \sumf_son,1+2*sum_son\\sumf_son,1-f_son_i,1+f_son_i,0+2*(sum_son-1) \endcases \]

\[ f[i][1]=min\begincases f_i,0\\sumf_son,1-f_son_i,1+f_son_i,0-f_son_j,1+f_son_j,0+2*(sum_son-2) \endcases \]


T2

对于二进制串a,b,他们之间的海明距离是指两个串异或之后串中1的个数

计算两个串之间的海明距离的时候,他们的长度必须相同。现在我们给出N个不同的二进制串,请计算出这些串两两之间的最短海明距离。

一开始看错题了。以为海明距离是两个串异或之后的值。以为是一道水题,码完trie之后发现不对劲,才发现题目看错了...

比赛剩20分钟的时候,预知未来看到fjy50分决定打暴力,就在trie上面搜索,加了一个最优性剪枝,剩6分钟的时候交上去,居然切了!

出来一看题解

至于证明……只能说:如果出题人出人工数据来卡暴力的话,并不能使n=10000,ans=20,因为这些数字两两不重复,所以当ans=20,n必定为2。以此类推……极限数据不会太坑。

? ==记住:骗分最神奇,暴力出奇迹。==

By 海明距离Solution

.......牛逼

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

struct qy

    int ch[2];
    int bz;
;

int T,i,j,n,x,tot,ans,s,k;
qy trie[100005];
int str[100005][35];
char ch[6];

void insert(int k)

    int x=0;
    for (int i=1;i<=20;i++)
    
        if (trie[x].ch[str[k][i]]==0)
        
            trie[x].ch[str[k][i]]=++tot;
            trie[tot].ch[0]=trie[tot].ch[1]=trie[tot].bz=0;
        
        x=trie[x].ch[str[k][i]];
    
    trie[x].bz=1;


void dg(int k,int x,int depth,int s)

    if (ans<=s) return;
    if (depth==20) ans=s;
    if (trie[x].ch[str[k][depth+1]]!=0)
    
        dg(k,trie[x].ch[str[k][depth+1]],depth+1,s);
    
    if (trie[x].ch[str[k][depth+1]^1]!=0)
    
        dg(k,trie[x].ch[str[k][depth+1]^1],depth+1,s+1);
    


int main()

    freopen("read.in","r",stdin);
    scanf("%d",&T);
    while (T--)
    
        scanf("%d",&n);
        for (i=1;i<=n;i++)
        
            scanf("%s",ch+1);
            for (j=1;j<=5;j++)
            
                if (ch[j]>='A') x=ch[j]-'A'+10;
                else x=ch[j]-'0';
                for (k=1;k<=4;k++)
                
                    if ((x&(1<<(4-k)))!=0)
                    str[i][(j-1)*4+k]=1;
                    else
                    str[i][(j-1)*4+k]=0;
                
            
        
        tot=0;
        trie[0].ch[0]=trie[0].ch[1]=trie[0].bz=0;
        ans=10000000;
        insert(1);
        for (i=2;i<=n;i++)
        
            dg(i,0,0,0);
            insert(i);
        
        printf("%d\n",ans);
    

ps:cgh的部分代码

for i:=1 to min(500,n) do
begin
         for j:=1 to min(500,n) do
               begin
      
               end;
end;

......牛逼

什么垃圾数据啊!(摔

T3

一个关于n个元素的排列是指一个从1, 2, …, n到1, 2, …, n的一一映射的函数。这个排列p的秩是指最小的k,使得对于所有的i = 1, 2, …, n,都有p(p(…p(i)…)) = i(其中,p一共出现了k次)。

例如,对于一个三个元素的排列p(1) = 3, p(2) = 2, p(3) = 1,它的秩是2,因为p(p(1)) = 1, p(p(2)) = 2, p(p(3)) = 3。

给定一个n,我们希望从n!个排列中,找出一个拥有最大秩的排列。例如,对于n=5,它能达到最大秩为6,这个排列是p(1) = 4, p(2) = 5, p(3) = 2, p(4) = 1, p(5) = 3。

当我们有多个排列能得到这个最大的秩的时候,我们希望你求出字典序最小的那个排列。对于n个元素的排列,排列p的字典序比排列r小的意思是:存在一个整数i,使得对于所有j < i,都有p(j) = r(j),同时p(i) < r(i)。对于5来说,秩最大而且字典序最小的排列为:p(1) = 2, p(2) = 1, p(3) = 4, p(4) = 5, p(5) = 3。

花了10分钟理解题意,然后发现其实p就是一个个简单环,问题转化为:

把n拆成几个正整数的和(正整数可以为1),这些正整数的最小公倍数就是我们要的最大的秩

然后打暴力发现每一个正整数要么是1,要么是某个质数的某个次幂。

可以用dp

设f[I,j]表示我们处理到第i个质数、当前和为j所能获得的最大秩。则f[I,j]=max(f[i-1,j-w]*w)。(这里的w是简写)。而我们要求排列的话,只需记录一下每个状态是由哪个状态转移过来的,最后还原即可。

By kqp题解

兴奋得以为能切

然鹅...

第五步:注意事项

f的值很大很大,大过long long,所以我们要把f的值转为自然对数来做。由于自然对数是单调函数,所以比较大小的方式一毛一样。

Ps2:题解的实质在第四步。

By kqp题解

...幹

我把题解的前四步都想到了,就差第五步了,居然只给我40分。

心累啊

Ps:

其实不用自然对数也可以。由于这道题最大只有\(10^136\) ,用double存的下,又因为存的是乘积,一两个数的差别会有很大影响,所以把f的类型改为double即可

#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;

long long bz[10005],l[10005],i,j,s,n,k,ans1,ans2,T;
double f[1305][10005],ans;
int from[1305][10005][3];
long long list[10005];

void write(long long x,long long y)

    if (from[x][y][2]!=0)
    
        write(from[x][y][1],from[x][y][2]);
    
    list[++list[0]]=from[x][y][0];


int main()
 
    freopen("read.in","r",stdin);
    for (i=2;i<=10000;i++)
    
        if (!bz[i]) 
        
            l[++l[0]]=i;
        
        for (j=1;j<=l[0];j++)
        
            if (l[j]*i>10000) break;
            bz[l[j]*i]=1;
            if (i%l[j]==0) break;
        
    
    f[0][0]=1;
    for (j=1;j<=10000;j++)
    
        f[0][j]=1;
        from[0][j][0]=1;
        from[0][j][1]=0;
        from[0][j][2]=j-1;
    
    for (i=0;i<=l[0]-1;i++)
    
        for (j=0;j<=10000;j++)
        
            for (k=l[i+1];k+j<=10000;k=k*l[i+1])
            if ((double)k*f[i][j]>f[i+1][j+k])
            
                f[i+1][j+k]=(double)k*f[i][j];
                from[i+1][j+k][0]=k;
                from[i+1][j+k][1]=i;
                from[i+1][j+k][2]=j;
            
        
        for (j=0;j<=10000;j++)
        
            if (f[i+1][j]<f[i][j])
            
                f[i+1][j]=f[i][j];
                from[i+1][j][0]=from[i][j][0];
                from[i+1][j][1]=from[i][j][1];
                from[i+1][j][2]=from[i][j][2];
            
        
    
    scanf("%lld",&T);
    while (T--)
    
        scanf("%lld",&n);
        ans=ans1=ans2=0;
        list[0]=0;
        write(l[0],n);
        sort(list+1,list+1+list[0]);
        s=0;
        for (i=1;i<=list[0];i++)
        
            for (j=2;j<=list[i];j++)
            
                printf("%lld ",s+j);
            
            printf("%lld ",s+1);
            s+=list[i];
        
        printf("\n");
    
    return 0;

ymqtql%%%

T4

比完赛又加了一道题。

我们会给定一个N×M的矩阵,你需要从这个矩阵中找出一个P×P的子矩阵,使得这个子矩阵的每一列和每一行都是回文序列。

对于所有数据 1 ≤ N, M ≤ 300

题目挺简单,先在中间补上0方便处理,预处理ma_row[i][j]和ma_col[i][j]表示以(i,j)为中心在行或列的最长回文串。再枚举点(i,j)作为整个子矩阵的中心,从(i,j)向上下左右找ma_row[i\(\pm\)x][j]和ma_col[i][j\(\pm\)x]的最小值就好了。

理论复杂度最高为O(\(8n^2\)),272ms。

而那些理论复杂度最高为O(\(n^5\))的做法都是20~30ms的。

又一道欺诈题:)

小结

  1. 认真看题
  2. ==骗分最神奇,暴力出奇迹。==
  3. 存特别大的,精度要求不高的数据可以用double/long double
  4. 注意空间!!!

以上是关于6.29总结的主要内容,如果未能解决你的问题,请参考以下文章

6.28 6.29

6.29

6.29

6.29 m

Zabbix安装和配置

个人日志-6.29