zoukankan      html  css  js  c++  java
  • 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]=minegin{cases} {sum{f_{son,1}}+2*sum_{son}}\ {sum{f_{son,1}}-f_{son_i,1}+f_{son_i,0}+2*(sum_{son}-1)} end{cases} ]

    [f[i][1]=minegin{cases} {f_{i,0}}\ {sum{f_{son,1}}-f_{son_i,1}+f_{son_i,0}-f_{son_j,1}+f_{son_j,0}+2*(sum_{son}-2)} end{cases} ]


    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
    ",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("
    ");
    	}
    	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. 注意空间!!!
  • 相关阅读:
    【Qt开发】01-第一个Qt程序Hello World!
    Git使用总结(三):协同开发常见冲突
    公钥、私钥、数字签名、数字证书、对称与非对称算法、HTTPS
    通俗理解TCP的三次握手
    Flink安装极简教程-单机版
    程序员工资那些事!
    vim实战:插件安装(Vundle,NerdTree)
    Git使用总结(二):分支管理
    Git使用总结(一):简介与基本操作
    C++11并发编程:多线程std::thread
  • 原文地址:https://www.cnblogs.com/leason-lyx/p/11148301.html
Copyright © 2011-2022 走看看