补题+知识点hash康托展开

D. Period

题意要求求出在修改所给的位置p上的字符之后,字符串又多少个周期。

首先又判断,整个字符串不能超过两个周期,因为修改之后的字符是原字符串中所没有的,那么对于修改位置p上的字符而言,每个周期所对应的最后一个字符为p前的一个字符,那么就是看前p-1个字符或者后n-p个字符中能相互对应的有几个。

对于样例ccpc而言,由于c是对应的那么ans[1]++,说明进行周期处理后能对应的字符串长度为1的有1个,比如对于字符串abababa来说ans[1]++,ans[3]++。那么当修改的位置为4的时候,周期就能是6和2一共两个。现在就是如何求前缀和后缀相等的长度的数量,kmp

kmp的next数组表示的含义就是当第i为不匹配的时候前面的最长相等的 前缀和后缀的长度,

即对abababa来说

i01234567
abababa
next-10010303

那么对于这个串而言,我们需要统计当next[n]不匹配的时候前面相等的长度是多少,然后继续此过程即可。

之后,对整个ans数组作前缀和,ans[x]即表示前后缀相等的长度小于等于x的个数为多少。

然后对于每次询问,处理出其表示的最多的相等的长度,即min(p-1,n-p);(防止中间出现交叉)

c++里next为保留关键字,不能使用。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
const int N=1e6+5;
int n,nxt[N],ans[N];
string s;
void kmp(){
	int i=0,j=-1;
	nxt[0]=-1;
	while(i<n){
		if(j==-1||s[i]==s[j]){
			i++;j++;nxt[i]=j; 
		}else j=nxt[j];
	}
	int k=nxt[n];
	while(k){
		ans[k]++;k=nxt[k];
	}for(int i=1;i<=n;i++)ans[i]+=ans[i-1];
}
int main(){
	cin>>s;
	n=s.size();
	kmp();
	int m;
	cin>>m;
	while(m--){
		int p;
		scanf("%d",&p);
		p=min(p-1,n-p);
		cout<<ans[p]<<"\n";
	}
} 

J. Circular Billiard Table

首先对于题目圆进行分析可得,每两次碰撞之间形成的圆心角的大小即可2*β,那么假设在圆中碰撞了n次那么所形成的圆心角的和就是2*n*β,要保证能够除去,则2*n*β就必须为360的整数倍

\frac{a}{180*b}*n=k,k为整数,且要求出满足最小的k的n,那么,首先__gcd(a,180*b)可以得到化简之后的分母最小,那么n==180*b/gcd可得到的就是n的最小值,又因为在进入的时候就等效于碰撞了一次,那么输出n-1即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
const int N=2e6+5;
const int mod=1e9+7;
int in[N];
void solve(){
	ll a,b;
	scanf("%lld%lld",&a,&b);
	ll n=180*b/__gcd(180*b,a);
	cout<<n-1<<"\n";
}
int main(){
	int t=1;
	cin>>t;
	while(t--){
		solve();
	}
}

M. 810975

首先一个引理为:

将n个球分为m组,且保证每一组所包含的球数为正整数,那么考虑隔板法。

n个球中有n-1块隔板,加入进去m-1块即可C(n-1,m-1)

同时式子\sum_{i=1}^{i=m}x_i=n表示对m组中的球数求和式子成立。(此时的x为正整数)

那么推广到非负整数即可。

若此时x∈非负整数,那么x+1∈正整数,那么\sum_{i=1}^{m}(x_i+1)=n+m

\sum_{i=0}^{m}y_i=n+m        (其中y为正整数),那么此时y为正整数解(也就是x为非负整数)的方案数为

C(n+m-1,m-1)

得将n个球划分为m组,保证每一组具有的球数为非负整数的方案数为C(n+m-1,m-1)

题目中可以用败场来分割胜场,那么共有p=n-m+1个组,m个球,且保证每个组的球数要<=k,并且要至少有一组的球数=k

首先,对于m个球p个组的非负整数划分为C(m+p-1,p-1),

此时考虑容斥,枚举有i个组的球数大于k。我们先将球拿出i*k个,然后对于剩余的球数进行非负整数划分,然后即可保证此时的方案数为至少有i组的球数是>=k的。

当i为奇数的时候,我们需要总方案数减掉C(m-i*k+p-1,p-1)*C(p,i).

当i为偶数的时候,我们需要总方案数加上C(m-i*k+p-1,p-1)*C(p,i).

定义f(n,m,k)表示小于k的方案数,那么ans=f(n,m,k+1)-f(n,m,k)

最后注意特判

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
const int N=1e5+5;
const int mod=998244353;
ll fac[N]={1,1},finv[N]={1,1};
ll qpow(ll x,ll k){
	ll res=1;
	while(k){
		if(k&1)res=res*x%mod;
		x=x*x%mod;
		k>>=1;
	}return res;
}
void init(){
	for(int i=2;i<=N;i++){
		fac[i]=fac[i-1]*i%mod;
	}
    finv[N]=qpow(fac[N],mod-2);
    for(int i=N-1;i>=2;i--){
        finv[i]=(finv[i+1]*(i+1))%mod;
    }
}
ll c(int n,int m){
	if(m>n||m<0)return 0;
    return fac[n]*finv[m]%mod*finv[n-m]%mod;
}
ll f(int n,int m,int k){
	int p=n-m+1;
	ll ans=c(m+p-1,p-1);
	for(int i=1;i<=p;i++){
		ll sum=c(p,i)*c(m-i*k+p-1,p-1)%mod;
		if(i&1)ans=(ans-sum+mod)%mod;
		else ans=(ans+sum)%mod;
	}return ans;
}
int main(){
	int n,m,k;
	cin>>n>>m>>k;
	init(); 
	if(!k){
		if(!m)cout<<"1\n";
		else cout<<"0\n";
	}else if(k>m)cout<<"0\n"; 
	else cout<<(f(n,m,k+1)-f(n,m,k)+mod)%mod;
} 

知识点:

hash:

康托展开:实现的是全排列和自然数之间的一一对应的关系,即双射。

其中,an表示的是在该位置后有几个数比其要小的数的个数。

例如:  5 2 4 1 3                 那么进行康托展开后可得

x=4*4!+1*3!+2*2!+0*1!+0*0!=106,那么106就表示该序列在全排列中的序号,

当然,全排列的序号从0开始,即该序列是全排列的第107个序列、

这样可log级别下得到该排列的顺序,

并且由于在dfs或bfs进行的过程中,我们需要保存状态,那么当状态数难以直接进行处理的时候,我们可以采用这种方法来进行该状态与自然数的双射关系,例如一个3*3的1-9的数字矩阵,如果我们用直接拼接来保存状态,那么我们需要1亿的空间进行保存,而其不同的状态只有9!种,无疑是浪费了空间。

康托逆展开

例如上述的5 2 4 1 3 得到的106,那么我们如何从106得知该序列为5 2 4 1 3 呢

从上式进行反推,104/4! = 4 ……10    除以4!得到商为4余数为10,商即是an,那么表示后面有4个比其小的,其值即为5,

10/3!=1…… 4  表示后方有1个比其小的,那么它就是2

4/2!=2…… 0   表示后方有2个比其小的,那么它就是4

0/1!=0…… 0 表示后面有0个比它小的,那么它就是1

0/0!=0…… 0表示后面有0个比它小的,那么它就是3

得到序列5 2 4 1 3建立起了双射关系。

在逆展开的时候可以先将数加入vector中,然后对于得到的商k,就直接取vector[k],然后删掉vector中的此元素即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅游社交小程序功能有管理员和用户。管理员有个人中心,用户管理,每日签到管理,景点推荐管理,景点分类管理,防疫查询管理,美食推荐管理,酒店推荐管理,周边推荐管理,分享圈管理,我的收藏管理,系统管理。用户可以在微信小程序上注册登录,进行每日签到,防疫查询,可以在分享圈里面进行分享自己想要分享的内容,查看和收藏景点以及美食的推荐等操作。因而具有一定的实用性。 本站后台采用Java的SSM框架进行后台管理开发,可以在浏览器上登录进行后台数据方面的管理,MySQL作为本地数据库,微信小程序用到了微信开发者工具,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得旅游社交小程序管理工作系统化、规范化。 管理员可以管理用户信息,可以对用户信息添加修改删除。管理员可以对景点推荐信息进行添加修改删除操作。管理员可以对分享圈信息进行添加,修改,删除操作。管理员可以对美食推荐信息进行添加,修改,删除操作。管理员可以对酒店推荐信息进行添加,修改,删除操作。管理员可以对周边推荐信息进行添加,修改,删除操作。 小程序用户是需要注册才可以进行登录的,登录后在首页可以查看相关信息,并且下面导航可以点击到其他功能模块。在小程序里点击我的,会出现关于我的界面,在这里可以修改个人信息,以及可以点击其他功能模块。用户想要把一些信息分享到分享圈的时候,可以点击新增,然后输入自己想要分享的信息就可以进行分享圈的操作。用户可以在景点推荐里面进行收藏和评论等操作。用户可以在美食推荐模块搜索和查看美食推荐的相关信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值