【LGR-069】洛谷 2 月月赛 II & EE Round 2 Div.2 A-C题解
洛谷2月月赛前三题题解。
T1:由最优性可知,我们每次一定要选出现最多次的字符进行操作,因此我们只需要找原始字符串中出现次数最多的字符,不停对其进行变换,直到其长度符合要求。
注意数据类型问题,由于变换的时候可能会超出$unsigned long long$的上限,因此我们需要判断其是否溢出。
1 #include<iostream> 2 #include<queue> 3 #include<cstdio> 4 #include<cstring> 5 #define int unsigned long long 6 #define N 1000005 7 using namespace std; 8 int n,ch[1005],l,ans,maxn; 9 char str[N]; 10 signed main() 11 { 12 ios::sync_with_stdio(0); 13 cin>>(str+1); 14 cin>>l; 15 n=strlen(str+1); 16 for(int i=1;i<=n;i++)ch[str[i]]++; 17 for(int i=0;i<=255;i++)maxn=max(maxn,ch[i]); 18 while(n<l) 19 { 20 ans++; 21 if((n+maxn)<n)break; 22 n+=maxn; 23 maxn*=2; 24 } 25 cout<<ans<<endl; 26 return 0; 27 } 28
T2:我认为这是本场比赛出的最好的题。
下面先说几个部分分的做法。
$Subtask 1$直接输出样例不解释。
$Subtask 2$枚举$i$,$j$,$k$,$l$,$O(n^4)$暴力计算即可。
$Subtask 3,4$这两个部分分是可以一起做的,也是考场上比较好想的。观察数据范围可知,值域很小,因此考虑直接统计。
为了方便说明,我们先记值域为$m$
预处理一个数组$F$,$F_i$表示$i$在这个数列中出现了多少次。根据$F$数组初步统计$or$和$and$和。同样,设$B_i$表示i这个数是多少对数的$or$值,$C$同理,只需简单的乘法原理即可算出这两个数组。
最后直接枚举统计即可。时间复杂度$O(m^2)$
正解:上面的那个方法看起来很牛逼,但是值域一大它就完了。
因此考虑位运算的性质。
位运算的操作实际上位与位之间是相互独立的,并不会互相影响。
对于每一位,我们手算出16种可能性,发现以下这些四元组$(i,j,k,l)$可以通过题目中的位运算操作得到$1$
$(1,1,0,0),(1,0,1,0),(1,0,0,1),(0,1,0,1),(0,1,1,0),(0,0,1,1),(1,1,1,0),(1,1,0,1),(1,0,0,0),(0,1,0,0)$
然后开个二维数组$cnt$,其中$cnt(i,j)$表示第i位为j的数的个数。
乘法原理计算出该位为$1$的情况数后乘上位权,相加即可。
别忘记取模!!!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define N 5000005 5 using namespace std; 6 int read() 7 { 8 int x=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} 11 return x*f; 12 } 13 unsigned int n,a[N],cnt[N][2],maxx,maxy,maxn,f[N]; 14 unsigned int ans=0; 15 signed main() 16 { 17 n=read(); 18 for(int i=1;i<=n;i++) 19 { 20 a[i]=read(); 21 for(int j=0;j<32;j++) 22 { 23 if(a[i]&(1<<j))cnt[j][1]++; 24 else cnt[j][0]++; 25 } 26 } 27 for(int i=0;i<32;i++) 28 { 29 int res=0; 30 res+=(long long)(cnt[i][1]*cnt[i][1]*cnt[i][0]*cnt[i][0])*6; 31 res+=(long long)(cnt[i][1]*cnt[i][1]*cnt[i][1]*cnt[i][0])*2; 32 res+=(long long)(cnt[i][1]*cnt[i][0]*cnt[i][0]*cnt[i][0])*2; 33 ans+=(1<<i)*res; 34 } 35 cout<<ans<<endl; 36 return 0; 37 }
T3:很烦人的dp。
就这东西让我$270->170$(分数)。
我考场上第一反应:
$f[i][0/1/2/3]$表示长度为i的字符串,以;)]}为结尾的"语句"有多少个
$g[i][0/1/2/3]$表示长度为i的字符串,以;)]}为结尾的"程序片段"有多少个
$h[i][0/1/2/3]$表示长度为i的字符串,以;)]}为结尾的"语句块"有多少个
$q[i][0/1/2/3]$表示长度为i的字符串,以;)]}为结尾的"函数"有多少个
$w[i][0/1/2/3]$表示长度为i的字符串,以;)]}为结尾的"值"有多少个
然后对应转移。
结果7那个样例我输出141,当场心态崩掉。
发现离结束只有10分钟
然后就自闭了。
赛后,伟大的$4404$同志帮我找到了问题:
考虑如下字符串:([]{});。
然后你会发现这可以通过两种方法构得。
究其原因,是因为$A$是函数,所以$A$是个值,所以$(A)$是个函数也是个值。
所以"值"更新的时候无需再加一遍"函数"
然后AC之后发现,第二维没卵用。
自闭了。
233333333333333333333333333333333333333~~~~
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define int unsigned long long 5 #define N 1000006 6 using namespace std; 7 int n,f[N],g[N],h[N],q[N],w[N],ans; 8 signed main() 9 { 10 cin>>n; 11 f[1]=1; 12 g[0]=1; 13 for(int i=1;i<=n;i++) 14 { 15 if(i>1)q[i]+=h[i-2]; 16 if(i>3)q[i]+=h[i-4]; 17 if(i>1)q[i]+=q[i-2]; 18 w[i]+=q[i]; 19 if(i>1)w[i]+=w[i-2]; 20 if(i>1)h[i]+=g[i-2]; 21 f[i]+=w[i-1]; 22 f[i]+=h[i]; 23 for(int j=0;j<=i;j++)g[i]+=g[j]*f[i-j]; 24 } 25 cout<<g[n]<<endl; 26 }