这次比赛,看到了自己跟大家的差距,所谓的大神是从一点一滴练起的。不是说钻研得多高深,就多牛;而是所有的题干细节处理到位,所有能做的题1A,向bin神学习吧,同志们!一起加油!

说明:这个题解,有我自己的代码,也有bin神的,axp巨巨的,Tonny巨巨,还有好多帮助过我的人,希望群巨多多参与题解的建设,方便自己做总结,也方便群巨学习。


话不多说。从每一题开始(题意全部略去)

第一天

A.模拟。对3种情况,分别进行处理。容易出现的错误有:注意当队伍中已经没人的时候请忽略第2种事件,每组数据新开始的时候队伍中人数都为0!另外,我一开始实现的代码是用queue写的,不知道为什么超时了。。。估计是迭代器比较慢。AC代码如下:

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
#include <queue>
#include <stack>
using namespace std;

#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
#define For1(i,a,b) for (i=a;i<b;i++)
#define For2(i,a,b) for (i=a;i<=b;i++)
#define Fill(x,a) memset(x,a,sizeof(x))
#define inf 99999999
#define pi 3.1415926535897932384626433832795028841971

typedef long long LL;
const int maxn=10000050;

int head,tail;
int num[maxn];

int main(){
	//freopen("input.txt","r",stdin);
	int t,n,a,b,k;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		memset(num,0,sizeof(num));
		head=0,tail=0;
		while(n--){
			scanf("%d",&a);
			if (a==1){
				scanf("%d",&b);
				num[tail]=b;
				tail++;
			}
			else if (a==2){
				if (head<tail) head++;
			}
			else{
				scanf("%d",&k);
				if (head+k<=tail) printf("%d\n",num[head+k-1]);
				else puts("na li you zhe me duo ren");
			}
		}
	}
	return 0;
}

细节:注意head<tail这句,是处理重点语句的。我当时写了等号,意味着没有处理一开始就是选项2的情况,题意中说明了是有这种可能的。


B.按照题意,对输入的数字进行转换。我的代码很挫,改了又改最后对的。对于n=1000的这种小数据,告诉大家一个1A的方法,用freopen形成一个1到1000的测试数据,看看答案是不是有规律连续的即可。

我的代码:

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
#include <queue>
#include <stack>
using namespace std;

#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
#define For1(i,a,b) for (i=a;i<b;i++)
#define For2(i,a,b) for (i=a;i<=b;i++)
#define Fill(x,a) memset(x,a,sizeof(x))
#define inf 99999999
#define pi 3.1415926535897932384626433832795028841971

void Print(int n){
	int i,j,k;
	if (n<=26){
		printf("%c\n",n+'A'-1);
		return;
	}
	if (n>=27&&n<=26*27){
		for(j=1;j<=26;j++)
			for(k=1;k<=26;k++){
				if (j*26+k==n){
					printf("%c",j+'A'-1);
					printf("%c",k+'A'-1);
					puts("");
					return;
				}
			}
	}
	printf("A");
	for(j=1;j<=26;j++)
		for(k=1;k<=26;k++){
			if (j*26+k==n-26*26){
				printf("%c",j+'A'-1);
				printf("%c",k+'A'-1);
				puts("");
				return;
			}
		}
}
int main(){
	int t;
	int n;
	int ans[500];
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		Print(n);
	}
	return 0;
}

上上bin神的代码,每道题都可以学学不同的思路,有时候有了题解,不要懒,自己实现实现
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
	int n;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		if(n <= 26){
			printf("%c\n",(char)('A'+n-1));
		}
		else if(n <= 26+26*26){
			n -= 27;
			printf("%c%c\n",(char)('A'+n/26),(char)('A'+n%26));
		}
		else {
			n -= 26+26*26+1;
			printf("%c%c%c\n",(char)('A'+n/26/26),(char)('A'+(n/26)%26),(char)('A'+n%26));
		}
	}
    return 0;
}


又有一个巨巨提供的代码,觉得挺好的,供大家学习string的细节处理:

#include <cstdio>
#include <cstring>
int main()
{
    int N;scanf("%d",&N);
    while(N--)
    {
        int n,cnt=0;
        char s[16];
        scanf("%d",&n);
        while(n)
        {
            int m=n%26;
            if(!m)m=26;
            s[cnt++]=m+64;
            n=(n-m)/26;
        }
        for(int i=cnt;i;--i)
            putchar(s[i-1]);
        puts("");
    }
    return 0; 
}


C.这道题不需要题解的吧。。给大家提供个bin神快的诀窍。

sort(a,a+n)代表对a数组中的a[0]到a[n]从小到大排序

reverse(a,a+n)代表对a数组中所有元素反序存储。之后的。大家懂


D.这个题是比较好的一道题。有多种做法。

先说说思路。

1.bin神思路。dp[i][0]代表共i位数字,最后1位为0的不含有11的数目,dp[i][1]代表共i位数字,最后1位为1的不含有11的数目,这样,答案就是2^n-dp[i][0]-dp[i][1]。递推过程很好想。代码如下:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
const int MOD = 1e9+7;
int dp[1000010][2];
void Add(int &a,int b){
	a += b;
	if(a >= MOD)
		a -= MOD;
}
int two[1000010];

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    memset(dp,0,sizeof(dp));
	dp[1][0] = dp[1][1] = 1;
	two[1] = 2;
	for(int i = 2;i <= 1000000;i++){
		dp[i][0] = dp[i-1][1]+dp[i-1][0];
		if(dp[i][0] >= MOD)
			dp[i][0] -= MOD;
		dp[i][1] = dp[i-1][0];
		two[i] = 2LL*two[i-1]%MOD;
	}
	int T;
	int n;
	cin>>T;
	while(T--){
		scanf("%d",&n);
		int ans = two[n]-(dp[n][0]+dp[n][1])%MOD;
		ans = (ans%MOD+MOD)%MOD;
		printf("%d\n",ans);
	}
    return 0;
}

2.dfs处理所有小数据(或者手算打表)。发现ans[n]=2*ans[n-1]-f[n-1],其中f代表斐波那契数列,其中f[1]=f[2]=1。打表不贴代码,大家自己实现吧。

3.axp巨巨提供:光棍是递推,长度为n的有两种情况,110+长度为n-3不符合情况的,0或1加上长度为n-1符合要求的

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 1000100;
const int mod = 1000000007;
int arr[maxn];
int two[maxn];
int T,n;

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
	two[0]=1;
	for(int i=1;i<maxn;i++)
		two[i]=(two[i-1]*2)%mod;
	
	arr[2]=1;
	for(int i=3;i<maxn;i++)
	{
		arr[i]=( (2*arr[i-1])%mod + two[i-3]-arr[i-3] )%mod;
		while(arr[i]<0)
			arr[i]+=mod;
	}

	cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		printf("%d\n",arr[n]);
	}
    return 0;
}


5.物理题。。大家注意,题中条件少,意味着需要自己找。判断要求为:炮弹45度出发打不中的话就肯定打不中了(高中物理)。就是求以v斜向上45度发射,求最大距离x与d的关系。x=v^2/g。分解成水平速度v0和垂直速度vy。大家都会的。细心就好


6.我大概懂了,axp巨巨教的。每次循环都改动字符串,就是第i位和第len-k+i相同所以就统计所有相隔len-k的字符,选出出现次数最多的,把这些字符都修改为这个字符,统计要修改的次数就是答案了。代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

int ans;
const int maxn = 1100;
char ch[maxn];
int T,k;
int arr[26];
int be;
int l;

int f(int x)
{
	memset(arr,0,sizeof(arr));
	int re=0;
	int r=0;
	while(x<l)
	{
		r++;
		arr[ch[x]-'A']++;
		x=be+x;
	}
	for(int i=0;i<26;i++)
		re=max(re,arr[i]);
	return r-re;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>T;
	while(T--)
	{
		scanf("%s%d",ch,&k);
		l=strlen(ch);
		be=l-k;
		ans=0;
		for(int i=0;i<k && i<l-k;i++)
		{
			ans+=f(i);
		}
		printf("%d\n",ans);
	}
    return 0;
}

上面这题我一直没过是因为我用贪心思考的,判断的时候没有管到连续的修改值的问题。axp巨巨Hack我的数据是aaabbbccc 8。我的贪心代码比较渣,只关注了第i位和第i位匹配位和第len-i位的匹配位。没有想到链接的关系。Hack数据len=9,k=8,意思是连续的都相同,即为所有字符都一样。所以,贪心策略不知道实行多少次,没有贪心的正确性保证,只能用类似搜索的方法实现。


7.既然是颗树,既然是n=1000,dfs和bfs是不会有问题的。我的思路,用ans[i]记录,若等于0说明未访问过,若等于j说明走到i之前必须过j(由于是颗树),dfs时数目肯定不会大,因为每次扩展的节点是有限的。代码如下:

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
#include <queue>
#include <stack>
using namespace std;

#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
#define For1(i,a,b) for (i=a;i<b;i++)
#define For2(i,a,b) for (i=a;i<=b;i++)
#define Fill(x,a) memset(x,a,sizeof(x))
#define inf 99999999
#define pi 3.1415926535897932384626433832795028841971

const int maxn=2000;
int t,n,s;
int mp[maxn][maxn];
int ans[maxn];

void dfs(int start){
	int i,j;
	for(i=1;i<=n;i++)
		if (!ans[i]&&mp[start][i]){
			ans[i]=start;
			dfs(i);
		}
}

int main(){
	//freopen("input.txt","r",stdin);
	scanf("%d",&t);
	int a,b;
	int i,j,k;
	while(t--){
		memset(mp,0,sizeof(mp));
		memset(ans,0,sizeof(ans));
		scanf("%d%d",&n,&s);
		ans[s]=-1;
		for(i=1;i<n;i++){
			scanf("%d%d",&a,&b);
			mp[a][b]=mp[b][a]=1;
		}
		dfs(s);
		for(i=1;i<=n;i++)
			printf("%d%c",ans[i],i==n?'\n':' ');
	}
	return 0;
}

8.大大的水题。给个式子就好了。

num[1]=1;
num[2]=2;
for(i=3;i<=1000;i++)
     num[i]=num[i-1]+i-1;

小数据打表就好。



初赛的题解就到这里。谢谢bin神,axp巨巨,Tonny巨巨和群巨的帮助和支持。希望大家也能分享自己的AC和各种错误,一起提高。

Hint:比赛就是这样,还记得我初中数学老师说的,打不了满分就不要说题目简单。放到ACM也一样,这道题你不是1A就没有资格说它简单,这场比赛没有AK,有错误就不够完美,赛后题解只是补充学习,更多的是大家平时的细节注意,大家为着梦想加油!