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的。
又一道欺诈题:)
小结
- 认真看题
- ==骗分最神奇,暴力出奇迹。==
- 存特别大的,精度要求不高的数据可以用double/long double
- 注意空间!!!
以上是关于6.29总结的主要内容,如果未能解决你的问题,请参考以下文章