CSU2019_6月月赛总结
还有一半的时间逃出来开始写总结(难题太难不会做溜了溜了)
一A了两个水题,还是比较欣慰,最少比原来的连水题都疯狂wa进步很多,但是难题还是没思路,有思路代码不会实现。
此外,觉得水题虽水,但也需要运用技巧,像E题,hint 中重要的事情说三遍写出了 题目对空间大小的限制 2MB ,显然开数组绝对会炸,写完了开数组排序取中值就觉得不对,立刻换思路,不开数组解决。
E题AC代码: (一个求多余一半的数的个数的问题)
//
// main.cpp
// 2_E
//
// Created by 陈冉飞 on 2019/6/2.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
int total;
scanf("%d",&total);
int res = 0,cnt = 0,num;
for (int i = 0; i<total; i++) {
scanf("%d",&num);
if (cnt == 0){
res = num;
}else if(res == num){
cnt++;
}else {
cnt--;
}
}
cout<<res<<endl;
return 0;
}
这次的D题,同样也是一道水题,最一开始看到别人疯狂a 自己却一点思路都没有慌的一批,后来一想,怎么都是菜,不会做就不会做了,冷静下来思考了一下就是逐次选取偶数,每次对n进行除二的操作,除出来的数即为偶数,即可以提取的k,给他加到ans 中。最后再输出ans即可。
D题AC代码:
//
// main.cpp
// 2_D
//
// Created by 陈冉飞 on 2019/6/2.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <iostream>
using namespace std;
#define ll long long
int main(int argc, const char * argv[]) {
ll int n,ans = 0;
scanf("%lld",&n);
while (n>=2) {
if (n%2 == 0) {
ans+=n/2;
}
else{
n--;
ans+=n/2;
}
n/=2;
}
cout<<ans<<endl;
return 0;
}
附加AB题的题解:
A题:
-csuoj2334 正解是Bfs+剪枝。好像不剪枝直接Bfs爆搜也能过??? Bfs: 直接枚举每个方向的速度bfs搜索下去即可。 剪枝:在状态拓展过程中可以去掉一些重复无用的搜索。走一条线的时候,如果出现了前方有一个位子(x,y,z),使得从起点走到这个点的时间小于等于当前点时间的话,无需继续向那个方向继续走了。因为那个方向的取优方案可以通过点(x,y,z)来更新,所以多余步骤可以删除。同时如果搜索到终点的话也可直接退出。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
char s[110][110][110];
int dp[110][110][110],dx[]={0,0,1,-1,0,0},dy[]={1,-1,0,0,0,0},dz[]={0,0,0,0,-1,1},n,m,l,w;
struct Q{
int x,y,z;
}u,v,ss,tt;
queue<Q>q;
void bfs(){
int i,j;
memset(dp,33,sizeof(dp));
dp[ss.z][ss.x][ss.y]=0;
while(!q.empty()) q.pop();
for(q.push(ss);!q.empty();q.pop()){
u=q.front();
if(u.x==tt.x&&u.y==tt.y&&u.z==tt.z) break;
for(i=0;i<6;i++){
for(j=1;j<=w;j++){
v.x=u.x+dx[i]*j;
v.y=u.y+dy[i]*j;
v.z=u.z+dz[i]*j;
if(v.x<0||v.x>=n||v.y<0||v.y>=m||v.z<0||v.z>=l) break;
if(s[v.z][v.x][v.y]=='#'||dp[v.z][v.x][v.y]<=dp[u.z][u.x][u.y]) break;
if(dp[v.z][v.x][v.y]>dp[u.z][u.x][u.y]+1){
dp[v.z][v.x][v.y]=dp[u.z][u.x][u.y]+1;
q.push(v);
}
}
}
}
if(dp[tt.z][tt.x][tt.y]<=1e6) printf("%d\n",dp[tt.z][tt.x][tt.y]);
else printf("-1\n");
}
int main(){
int i,j,k,t;
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d",&n,&m,&l,&w);
for(k=0;k<l;k++) for(i=0;i<n;i++) scanf("%s",&s[k][i]);
for(i=0;i<n;i++){
for(j=0;j<m;j++){
for(k=0;k<l;k++){
if(s[k][i][j]=='S'){
ss.x=i;
ss.y=j;
ss.z=k;
}
if(s[k][i][j]=='T'){
tt.x=i;
tt.y=j;
tt.z=k;
}
}
}
}
bfs();
}
return 0;
}
B题:
csuoj2336 由于只考虑出现次数的奇偶,所以可以处理出一个26位的二进制数来表示每个字符串,表示每个字母出现次数的奇偶。然后问题就转为求有多少个子集的异或值为0。很容易想到枚举每个子集,但子集数会有2^n个,直接枚举在这题里是过不了的。 这里需要将字符串分为两部分,再分别枚举这两部分的子集的xor值,用个map记录每个xor值的出现次数,这样时间复杂度就降为了 log n * 2^(n/2) 了。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
char s[110];
int A[30],cnt[30];
map<int,int>ma;
int main(){
int i,t,n,len,j,sum;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%s",s);
len=strlen(s);
memset(cnt,0,sizeof(cnt));
for(j=0;j<len;j++) cnt[s[j]-'a'+1]++;
sum=0;
for(j=1;j<=26;j++){
sum=sum*2;
if(cnt[j]%2) sum+=1;
}
A[i]=sum;
}
int k=n/2,m=n-n/2;
ma.clear();
int ans=0;
for(i=1;i<(1<<m);i++){
sum=0;
for(j=0;j<m;j++) if((1<<j)&i) sum^=A[j+k+1];
if(ma.find(sum)==ma.end()) ma[sum]=1;
else ma[sum]++;
if(sum==0) ans++;
}
for(i=1;i<(1<<k);i++){
sum=0;
for(j=0;j<k;j++) if((1<<j)&i) sum^=A[j+1];
if(ma.find(sum)!=ma.end()) ans+=ma[sum];
if(sum==0) ans++;
}
printf("%d\n",ans);
}
return 0;
}
F题:
数学推导:
x2+y2=r2
⇒y2=(r−x)(r+x)
令d=gcd(r−x,r+x)
则y2=d2∗r+xd∗r−xd
再令A=r+xd,B=r−xd
则y2=d2∗A∗B
考虑y2是完全平方数,d2是完全平方数,又gcd(A,B)=1那么A,B都是完全平方数。
设A=a2,B=b2
A+B=a2+b2
⇒2∗rd=a2+b2
考虑枚举2∗rd,这一步的复杂度是O(r√)的,然后再在[1,2∗rd‾‾‾‾‾√/2]的范围内枚举a,进而可以算出A,b,B,然后判断A,B是否互质,B是否为完全平方数,这样子就算出了第一象限的答案,然后将ans∗4+4,算是统计了每一个象限的并且加上了坐标轴上的四个点。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define llg long long
#define maxn 100010
#define yyj(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
llg ans,n;
inline llg getint()
{
llg w=0,q=0; char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-') q=1, c=getchar(); while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}
void calc(llg d)
{
for (llg a=1;a<=sqrt(d/2);a++)
{
llg A=a*a,B=d-A,b=sqrt(B);
if (b*b==B && __gcd(A,B)==1 && A!=B) ans++;
}
}
int main()
{
yyj("circle");
cin>>n;
for (llg i=1;i<=sqrt(n*2);i++)
if ((2*n%i)==0)
{
calc(i);
calc(2*n/i);
}
cout<<ans*4+4;
return 0;
}