牛客多校第一场

自用复习

Easy Integration

题意

我这种菜鸡看到第一眼以为就是求积分,结果完全错误
前导知识:逆元快速幂
在这里插入图片描述

代码(AC)

#include<bits/stdc++.h> 
#include<stdio.h>
using namespace std;
const int MOD = 998244353;
const int N = 1e6 + 5;

long long fac[2 * N];

long long power(long long a, long long b) {
	long long ans = 1;
	for(;b;b >>= 1) {
		if(b & 1)	
			ans = ans * a % MOD;
		a = a * a % MOD;
	}
	return ans;
}

int getinv(long long a) {
	return power( a ,MOD - 2);
}

int main() {
	int n;
	fac[0] = 1;
	for(int i  = 1;i < 2*N  ; i++) {
	//这个地方记得写N,我一开始脑残写到while里面写2*n+1,直接。。。
		fac[i] = fac[i-1] * i % MOD;
	}
	while(scanf("%d",&n) != EOF) {
		//cout<<fac[2 * n]<<endl;
		printf("%lld\n",fac[n] * fac[n] % MOD * getinv(fac[2*n+1]) % MOD);
	}
		
	return 0;
}

F-Infinite String Comparision

题意与分析

就是给定两个字符串a,b,求a后面接着无穷个a字符串的时候,b后面也有无穷个b字符串,求这两个字符串相对的大小,可以输出‘> < =’
就是硬写的,没用到什么算法,得到的教训是想好了再动手,否则就会一直狂错,而且都是小错
把比较长的那个字符串*2,然后把比较短的那个也凑成相同的长度,再直接比较即可

代码(AC)

#include<bits/stdc++.h>
using namespace std; 

int main() {
	string a,b;
	//freopen("./1.in","r",stdin);
	//freopen("./1.out","w",stdout);
	while(cin >> a >> b) {
		int l1 = a.size();
		int l2 = b.size();
		
		string ta = "", tb = "";
		if(l1 < l2) {
			b += b;
			l2 = 2 * l2;
			for(int i = 1; i <= l2 / l1; i++)
				ta += a;
			l1 = ta.size();
			int d = l2 - l1;
			
			tb = b;
			for(int i = 0; i < d; i++)
				ta += a[i];
		}
		else {
			a += a;
			l1 = l1 * 2;
			for(int i = 1; i <= l1 / l2; i++)
				tb += b;
			l2 = tb.size();
			
			int d = l1 - l2;
			ta = a;
			for(int i = 0; i < d; i++)
				tb += b[i];
		}
		//cout<<ta<<endl;
		//cout<<tb<<endl;
		if(ta < tb)
			puts("<");
		else if(ta == tb)
			puts("=");
		else
			puts(">");
		a.clear();b.clear();
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

水题到此结束QAQ

B-Suffix Array

题意与分析

题目给定一个字符串只包含a和b,分别记录该字符产每个后缀字符串经过一定处理后得到的字串,并且以字典序排序,最后输出排序结果(每个后缀最开始的字符的位置表示该后缀)。
处理后缀字符串的方式就是记录每个字母与离自己最近的相同字符的距离,并且必须是在该字符之前的字符(若没有则为0)。
在这里插入图片描述

后缀数组(板子)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;

int sa[N],Rank[N],height[N],tax[N],tp[N],n,m;
//rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置;
//Height[i] 排名为i的后缀与排名为(i-1)的后缀的最长公共前缀 
//tax[i] 计数排序辅助数组; tp[i] rank的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串
char a[N];

void rsort() {//基数排序 
	for(int i = 0; i <= m; ++i) tax[i] = 0;
	for(int i = 1; i <= n; ++i) tax[Rank[tp[i]]] ++;
	for(int i = 1; i <= m; ++i) tax[i] += tax[i-1];
	for(int i = n; i > 0; --i)  sa[tax[Rank[tp[i]]] --] = tp[i];
}

int cmp(int* f, int x, int y, int k) {
	return f[x] == f[y] && f[x+k] == f[y+k];
}

void get_sa() {
	for(int i = 1; i <= n; i++) tp[i] = i,Rank[i] = a[i];
	m = 127, rsort();//初始化sa,此时得出的sa反推出的rank代表每一位字母的排名 
	
	for(int w = 1; w <= n; w <<= 1) {
	//w代表子串长度,w=1代表每一个长度为2字符串,p代表rank中排名最低的数
	
		//从i开始表示至少有一个不存在的子串,直接置为i,正好排在tp数组最前面
		int p = 0;
		for(int i = n - w + 1; i <= n; ++i) tp[++p] = i;
		for(int i = 1; i <= n; i++)
			if(sa[i] > w) tp[++p] = sa[i] - w;//可以把tp数组补齐,tp是由sa求出的 
		
		rsort();//更新sa值
		swap(Rank, tp);//暂存rank,此时tp已经不重要了 
		Rank[sa[1]] = p = 1;//rank和sa互逆,这个式子一定成立
		
		//用sa逆推rank的值 
		for(int i = 2; i <= n; ++i)  Rank[sa[i]] = cmp(tp, sa[i], sa[i-1], w) ? p : ++p;
		if(p == n) break;
		m = p;
	}
}

void get_height() {//排名i与i-1的最长公共前缀 
	int j, k = 0;
	for(int i = 1; i <= n; height[Rank[i ++]] = k)
		for( k = k ? k-1 :k, j = sa[Rank[i] - 1]; a[i+k] == a[j+k]; ++k);
	//k=0,则从头比较,否则直接从第k个之后比较,之前的已经相等了 
}

int main() {
	scanf("%s",a+1);
	n = strlen(a+1);
	get_sa() ;
	//get_height();
	
	for(int i = 1; i <= n; i++)
		cout<<sa[i]<<" ";
	return 0;
}

代码(AC)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int tp[N],rk[N],sa[N],tax[N],a[N],n,m;
char s[N];

struct node {//分别存储某一个特定a和b的位置 
	int a,b;
}q[N];

void rsort() {
	for(int i = 0; i <= m; i++) tax[i] = 0;
	for(int i = 1; i <= n; i++) tax[rk[tp[i]]] ++;
	for(int i = 1; i <= m; i++) tax[i] += tax[i-1];
	for(int i = n; i >= 0; i--) sa[tax[rk[tp[i]]] --] = tp[i];
}

bool check(int *f, int x, int y, int k) {
	return f[x] == f[y] && f[x+k] == f[y+k]	;
}

void get_sa() {
	for(int i = 1; i <= n; i ++) tp[i] = i, rk[i] = a[i];
	m = 127;
	rsort();
	for(int w = 1, p = 1, i; p < n; w += w, m = p) {
		for(p = 0, i = n - w + 1; i <= n; i++) tp[++p] = i;
		for(i = 1; i <= n; i++)
			if(sa[i] > w)
				tp[++p] = sa[i] - w;
		rsort();
		for(i = 1; i <= n; i++) tp[i] = rk[i];
		rk[sa[1]] = p = 1;
		for(i = 2; i <= n; i++) rk[sa[i]] = check(tp, sa[i], sa[i-1], w) ? p : ++p;
	}
}

bool cmp(node x, node y) {
//因为后缀数组处理的是去掉最前面的01...10的所以需要在后面一个字母加1的位置比较 
	if(x.b - x.a == y.b - y.a) return rk[x.b+1] < rk[y.b+1]; 
	return x.b - x.a < y.b - y.a;
}

int main() {
	//freopen("./1.in","r",stdin);
	//freopen("./1.out","w",stdout);
	while(scanf("%d",&n) != EOF) {
		scanf("%s",s+1);
		int ap = -1, bp = -1; 
		for(int i = 1; i <= n; i++) {
			a[i] = 0;
			if(s[i] == 'a') {
				if(ap != -1) a[i] = i - ap;
				ap = i;//更新a最新一次出现的位置 
			}
			else if(s[i] == 'b'){
				if(bp != -1) a[i] = i - bp;
				bp = i;//更新b最新一次出现的位置 
			}
		}
		get_sa();
		rk[n+1] = -1;
		rk[n+2] = -2;
		ap = bp = n+1;
		for(int i = n; i > 0; i-- ) {
			if(s[i] == 'a') {
				q[i].a = i;
				q[i].b = bp;
				ap = i;
			}
			else if(s[i] == 'b'){
				q[i].a = i;
				q[i].b = ap;
				bp = i;
			}
		}
		sort(q+1, q+n+1, cmp);
		for(int i = 1; i <= n; i++) cout<<q[i].a<<" ";
		cout<<endl;
	}
	return 0;
}

I-1 or 2

标准题解:一般图匹配

代码

其实还没看懂那个匹配算法,于是硬嫖了一个能看懂的代码,代码意思就是从一个点开始往下走能够使题目给定的d数组全部减为0则是可行的,输出“Yes”,否则输出“No”,然后按照每个点出现的次数排序即可一定程度上剪枝(我的理解),因为不这么写好像直接结束不了,从出现次数最少的点开始深度遍历

#include<bits/stdc++.h>
using namespace std;
int d[105], f[105], num[105], flag, n, m;
//d题目给定,num保存每个点出现次数,f保存排序后的每个点,flag反映结果
bool vis[105];
vector<pair<int, int> > gr[55];

void dfs(int x) {
	int u = f[x];
	if(flag)	return;
	if(x == n+1) {
		flag = 1;
		return;
	}
	if(d[u] == 0)
		dfs(x+1);
	for(int i = 0; i < gr[u].size(); i++) {
		int now = gr[u][i].second;
		if( vis[now] )
			continue;
		int v = gr[u][i].first;
		if(d[v] > 0) {
			d[u]--, d[v]--;
			vis[now] = true;
			dfs(x);
			d[u]++, d[v]++;
			vis[now] = false;
		}
	}
}

bool cmp(int x, int y) {
	return num[x] < num[y];
}

int main() {
	//freopen("./1.in","r",stdin);
	//freopen("./1.out","w",stdout);
	while(scanf("%d %d",&n, &m) != EOF) {
		flag = 0;
		for(int i = 1; i <= n; i++) {
			f[i] = i; gr[i].clear(); 
			scanf("%d",&d[i]);
		}
		memset(num, 0, sizeof(num));
		memset(vis, false ,sizeof(vis));
		for(int i = 1; i <= m; i++) {
			int x,y;
			scanf("%d %d",&x,&y);
			gr[x].push_back(make_pair(y,i));
			gr[y].push_back(make_pair(x,i));
			num[x]++; num[y]++;
		}
		sort(f+1, f+n+1, cmp);
		dfs(1);
        if(flag)
            puts("Yes");
        else
            puts("No");
	}
	return 0;
}

H-Minimum-cost Flow(没写呢)

最小费用流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值