10.18

一、build

    这道题就是字符串的一些判重操作。给你n个字符串,每个字符串通过“/”分成若干个文件,不同层数文件名相同也认为是不同文件,相同层数不同文件名认为是不同文件,然后让你求到达每一层以后,你需要建多少个文件。n<=2000,字符串长度最大100.

(1)纯模拟。

    我们发现,如果你现在枚举到第i个字符串的某一个文件,如果这个文件不需要建的话,那么它从开头到目前这个位置一定是可以与之前的字符串匹配上的。为了方面比较,我们把所有字符串的第一个/忽略,然后在每一个字符串的最后添加一个/,这样的话,我们每次只需要先统计一下这个字符串出现/的次数,然后尝试匹配之前的字符串,在匹配的同时记录一下最大能匹配的/的数量,两者相减就是你至少需要新建的文件数量。复杂度N^2*100,思路简单答案计算方便,所以可以卡过。这个思路也是我在考场上现想出来的,确实思路是对的,但是因为一些细节没有处理好,挂了60分。那么你发现,比赛的时候就算是简单的模拟题,你也要确保你的思路完全正确性以及考虑全面,不然就白整。有的时候觉得处理起来好麻烦,可以尝试换一个思路,会轻松不少。今天我最开始写的是通过文件名前的/来判断文件的个数,所有就比之前的复杂,然后自己也没有考虑完所有的情况,所以就没A掉。

#include<bits/stdc++.h>
using namespace std;
char s[2100][210];int n,m,ans;
int main(){
	freopen("build.in","r",stdin);
	freopen("build.out","w",stdout);
	scanf("%d",&n);
	scanf("%s",s[1]+1);
	m=strlen(s[1]+1);
	s[1][m+1]='/';m++;
	for(int i=2;i<=m;i++) if(s[1][i]=='/') ans++;
	printf("%d\n",ans);
	for(int i=2;i<=n;i++){
		scanf("%s",s[i]+1);
		m=strlen(s[i]+1);int x=0,y=0;
		s[i][m+1]='/';m++;
		for(int j=2;j<=m;j++) if(s[i][j]=='/') x++;
		for(int k=1;k<i;k++){
			int temp=0;
			for(int j=2;j<=m;j++){
				if(s[i][j]!=s[k][j]){
					y=max(y,temp);break;
				}
				if(s[k][j]=='/') temp++;
			}
			y=max(y,temp);
		}
		ans+=x-y;
		printf("%d\n",ans);
	}
	return 0;
}

(2)map的应用

    我们可以使用一个map数组,我们发现枚举每一个字符串的时候,我们已经知道了这个文件的层数和文件名,如果我们可以快速查出符合这个条件的文件在之前出现过没有,那我们就可以很快的求出答案。对于每一个字符串,我们从头开始扫,以/ /为分界线,把中间的文件名用string存储起来,然后搜索一下这个出现过没,如果没有,就直接计算答案,然后把剩下的存入。如果出现,就继续。复杂度N^2logN。

(3)trie树

        这道题就是快速搜索字符串,显然使用trie树是最快的,复杂度N*100。每次插入字符串,在插入的同时,根据trie树的结构我们可以判断当前是否是新插入的点,仍然按照之前的那种思路,最后一个位置加上一个/,然后我们通过判断新插入的点的/的数量来加答案。

#include<bits/stdc++.h>
using namespace std;
int ch[100010][60],n,ans,tot=1;
char s[2100];
void insert(char* s){
	int len=strlen(s+1);s[len+1]='/';int p=1;
	for(int k=2;k<=len+1;k++){
		int cc=0;
		if(s[k]>='0'&&s[k]<='9'){
			cc=s[k]-'0'+1+26;
		}else if(s[k]=='/') cc=0;
		else cc=s[k]-'a'+1;
		if(ch[p][cc]==0){
			ch[p][cc]=++tot;
			if(cc==0) ans++;
		}
		p=ch[p][cc];
	}
}
int main(){
	freopen("build.in","r",stdin);
	freopen("build.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		insert(s);
		printf("%d\n",ans);
	}
	return 0;
}

二、orzzzq

    给你n个扇形,让你给这n个扇形分别染上颜色,每相邻两个扇形的颜色不能一样,然后你一共有m种颜料,问你方案数。当时看到这道题的时候,不知道为什么没有思路,当时首先大脑给直觉好像是组合数,但m种颜料,要求相邻颜色不同,没有见过这种模型,感觉很不可做就直接跳过去写第三题了。现在想想,好像傻了点。一般这种题,好不好写就看你设计的状态如何。其实之前做过一道类似的题,还是自己太菜了,没有联想到。我们考虑,其实我们可以先确定一个点染得颜色,不过具体哪个颜色我们不管,就知道它是一个确定的颜色,然后如果我们通过这个算出了给n个扇形都染上色的方案数,那我们只需要把它乘m就求出了答案。那么我们设f[i][0]表示第i个扇形染得与第一个扇形染色不同的方案数,f[i][1]表示第i个扇形染与第一个颜色相同的方案数。然后我们很容易发现,f[i][1]=f[i-1][0],f[i][0]=f[i-1][0]*(m-2)+f[i-1][1]*(m-1),我们发现递推式中当前状态由上一个状态转移过来,而且系数是固定不变的。很容易想到我们需要使用矩阵乘法来优化递推,设计一个1*2的矩阵存储状态,一个2*2的状态转移矩阵,然后递推到f[n]就是把状态矩阵乘上状态转移矩阵的(n-1)次方。使用快速幂加速乘法就好了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll t,n,m,f[2],a[2][2];
void mul(ll f[2],ll a[2][2]){
	ll c[2];
	memset(c,0,sizeof(c));
	for(int i=0;i<2;i++)
		for(int k=0;k<2;k++)
			c[i]=(c[i]+f[k]*a[k][i]%mod)%mod;
	memcpy(f,c,sizeof(c));
}
void mulself(ll a[2][2]){
	ll c[2][2];
	memset(c,0,sizeof(c));
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			for(int k=0;k<2;k++)
				c[i][j]=(c[i][j]+a[i][k]*a[k][j]%mod)%mod;
	memcpy(a,c,sizeof(c));
}
int main(){
	freopen("orzzzq.in","r",stdin);
	freopen("orzzzq.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&m);
		if(n==1){
			printf("%lld\n",m);
			continue;
		}
		f[0]=0;f[1]=1;
		a[0][0]=m-2;a[0][1]=1;
		a[1][0]=m-1;a[1][1]=0;
		n--;
		for(;n;n>>=1){
			if(n&1) mul(f,a);
			mulself(a);
		}
		printf("%lld\n",m*f[0]%mod);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值