集美大学第九届程序设计竞赛 部分题解(待补充)

B.https://ac.nowcoder.com/acm/contest/42400/B

题目大意,就是选取两个合法矩形,求总方案数为多少。

思路:可以很巧妙的转化一下问题,选取两个矩形,本质上就是在x和y轴各选四个点,外侧两个点可以认为就是大矩形的顶点,内部两个点认为是小矩形的的顶点,

则总方案数为C(n+1,4) * C(m+1,4),组合数直接求解即可.

思路转换很妙,记录一下。完整代码如下:

#include<bits/stdc++.h>

using namespace std;
#define int long long
const int N=2e5+10,mod=1e9+7;
int fac[N],infac[N];
int qmi(int a,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*a%mod;
		a=a*a%mod;
		k>>=1;
	}
	return ans;
}
void init()
{
	fac[0]=1;infac[0]=1;
	for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
	for(int i=1;i<N;i++) infac[i]=infac[i-1]*qmi(i,mod-2)%mod;
}
int C(int a,int b)
{
	if(a<b) return 0;
	return ((fac[a]*infac[b]%mod)*infac[a-b])%mod;
}

signed main()
{
	init();
	int t;
	cin>>t;
	while(t--)
	{
		int n,m;
		cin>>n>>m;
		cout<<C(n+1,4)*C(m+1,4)%mod<<endl;
	}
	return 0;
} 

M.https://ac.nowcoder.com/acm/contest/42400/M

题目大意,给定一个字符串,多次询问s[L,R]这一段字串是否为该字符串的循环节。提到循环节,很容易想到KMP, 因为我们可以根据ne数组来求最小循环节,但我写到这里卡住了,发现还需要用到字符串哈希,后面看了题解,里面给出了一个结论:假设最小循环节长为len, 则s[L,R]为循环节的条件为 (L-1)%len==0 && R%len==0 && n%(R-L+1)==0 ,具体证明没有给出,如有大佬证明了可以告诉我呀!(我就先当结论记住)此方法时间复杂度为O(n).

另外,本题还能用字符串hash来做,暴力的判断字串是否为循环节,时间复杂度为O(logn*n),也可以通过此题。

KMP代码:

#include<bits/stdc++.h>

using namespace std;
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1e6+10;

string s;
int ne[N];
int n,m;

void get_next()
{
    
	for(int i=2,j=0;i<=n;i++){
		while(j&&s[i]!=s[j+1]) j=ne[j];
		if(s[i]==s[j+1]) j++;
		ne[i]=j;
	}

}
signed main()
{
    
	//cin>>n>>m;
    scanf("%d%d",&n,&m);
	cin>>s;
    
	s=" "+s;
	get_next();
	while(m--)
	{
		int l,r;
		//cin>>l>>r;
        scanf("%d%d",&l,&r);
		l++; r++;
		int len=n-ne[n];
        //if(l==1&&r==n) printf("YES\n");
		if((l-1)%len==0&&r%len==0&&n%(r-l+1)==0) printf("YES\n");
		else printf("NO\n");
	}
	
	return 0;
} 

字符串hash:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
const int N = 1e6 + 5, P = 131;
int n, m;
char s[N];
ull p[N], h[N];
bool st[N];
ull get(int l,int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
void check(int x) {
    for (int i = 1; i <= n; i += x) {
        if (get(i, i + x - 1) != get(1, x)) return ;
    }
    st[x] = true;
}
int main() {
    scanf("%d%d", &n, &m);
    scanf("%s", s + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + s[i];
    }
    for (int i = 1; i <= n / i; i++) {
        if (n % i == 0) {
            check(i);
            check(n / i);
        }
    }
    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        l++, r++;
        if (st[r - l + 1] && get(1, r - l + 1) == get(l, r)) puts("YES");
        else puts("NO");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值