牛客练习赛88游记

牛客练习赛88游记

A - 活着的证据

题目大意:

将一个各数位都为 \(1\sim 8\) 的数逐位写成罗马数字,可以得到一个仅包含字符 VI 的串。
其中阿拉伯数字 \(1\sim 8\) 对应的罗马数字如下:

1 2 3 4 5 6 7 8
I II III IV V VI VII VIII
求使用不超过 \(S_V\)V,不超过 \(S_I\)I,能表示出不超过 \(N\) 位的最大数是多少?
\(\sum N\le 5\times 10^6\)

思路:

贪心。
显然 \(4\) 不可能出现,因为用 \(6\) 来取代更加划算。
V 尽量安排在高位。若 \(S_V < N\),则再将剩下的空位先用一个 I 来填上。
如果还有多余的 I,则尽量往前放,能放满 \(3\) 个就放 \(3\) 个,否则就放 \(2\) 个或者 \(1\) 个,直到 I 用完或者放不下更多 I 为止。
时间复杂度 \(\mathcal O(N)\)

源代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
	char ch;
	while(!isdigit(ch=getchar()));
	int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
const int N=5e6+1;
int v[N],i[N];
int main() {
	for(int T=getint();T;T--) {
		int V=getint(),I=getint();
		int n=std::min(V+I,getint());
		std::fill(&v[1],&v[n]+1,0);
		std::fill(&i[1],&i[n]+1,0);
		for(int j=1;j<=std::min(V,n);j++) v[j]++;
		for(int j=V+1;j<=n;j++) i[j]++;
		I-=std::max(n-V,0);
		for(int j=1;j<=n;j++) {
			if(I==0) break;
			if(I==1) {
				i[j]+=1;
				I-=1;
			} else if(I==2||i[j]==1) {
				i[j]+=2;
				I-=2;
			} else {
				i[j]+=3;
				I-=3;
			}
		}
		for(int j=1;j<=n;j++) {
			putchar('0'+v[j]*5+i[j]);
		}
		puts("");
	}
}

B - 寻寻觅觅寻不到

题目大意:

给定两个串 \(M\)\(C\) 和一个整数 \(K\)
问是否可以从 \(M\) 中选取某个长度为 \(K\) 的子串并将其位置移到末尾,使得到的新串与 \(C\) 相等。
\(\sum|M|,\sum|C|\le 3\times 10^5; 1\le K\le 6\)

思路:

基础的字符串哈希题。
显然若 \(|M|\ne |C|\) 则答案肯定是 NO
\(|M|=|C|\) 时可对两串分别进行哈希。由于 \(C\) 确定,则长度为 \(K\) 的子串确定。枚举该子串出现的位置,对两部分分别比较哈希值即可。
\(K\) 的范围限制似乎是不必要的。
时间复杂度 \(\mathcal O(\sum |M|+\sum |C|)\)

源代码:

#include<cstdio>
#include<cctype>
#include<cstring>
inline int getint() {
	char ch;
	while(!isdigit(ch=getchar()));
	int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
using uint64=unsigned long long;
constexpr int N=5e6+2;
constexpr uint64 base=233;
char s[N],t[N];
uint64 pwr[N],hashs1[N],hashs2[N];
inline uint64 gethash(const int &l,const int &r) {
	uint64 ret=0;
	for(int i=l;i<=r;i++) {
		ret=ret*base+s[i];
	}
	return ret;
}
int main() {
	pwr[0]=1;
	for(int i=1;i<=N-2;i++) {
		pwr[i]=pwr[i-1]*base;
	}
	for(int T=getint();T;T--) {
		scanf("%s %s",&s[1],&t[1]);
		int k=getint();
		const int n=strlen(&s[1]);
		if(n!=strlen(&t[1])) {
			puts("NO");
			continue;
		}
		uint64 hash1=0,hash2=0;
		for(int i=1;i<=n-k;i++) {
			hash1=hash1*base+t[i];
		}
		for(int i=n-k+1;i<=n;i++) {
			hash2=hash2*base+t[i];
		}
		for(int i=1;i<=n;i++) {
			hashs1[i]=hashs1[i-1]*base+s[i];
		}
		hashs2[n+1]=0;
		for(int i=n;i>=1;i--) {
			hashs2[i]=hashs2[i+1]+s[i]*pwr[n-i];
		}
		for(int i=1;i<=n-k+1;i++) {
			if(gethash(i,i+k-1)==hash2) {
				if(hashs1[i-1]*pwr[n-i-k+1]+hashs2[i+k]==hash1) {
					puts("YES");
					goto Next;
				}
			}
		}
		puts("NO");
		Next:;
	}
}

C - 踩不出足迹

题目大意:

\(n\)\(k\) 位二进制数。在相邻两数之间插入异或、同或运算符,使得最终结果最大。
\(n\le 10^6; k\le 64\)

思路:

同或相当于对异或结果逐位取反。
对于仅含取反、异或操作的算式,局部取反等于全局取反。
因此我们只需无脑将这些数全部异或起来,最后决定是否需要取反即可。
另外注意 \(k=64\) 时会爆 unsigned long long,需特判。
时间复杂度 \(\mathcal O(n)\)

源代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
using uint64=unsigned long long;
inline uint64 getint() {
	char ch;
	while(!isdigit(ch=getchar()));
	uint64 x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
int main() {
	const int n=getint(),k=getint();
	uint64 ans=0;
	for(int i=1;i<=n;i++) {
		ans^=getint();
	}
	if(k!=64) {
		printf("%llu\n",std::max(ans,(1llu<<k)-1-ans));
	} else {
		printf("%llu\n",std::max(ans,~ans));
	}
}

D - 都市的柏油路太硬

题目大意:

一张 \(n\) 个点、\(m\) 条边的连通双向图,定义一条路径的权值为该路径最长边的长度,点对的权值为两点间所有路径权值的最小值。
以所有点对间的权值为边权,建立 \(n\) 个点、\(\frac{n(n-1)}{2}\) 条边的新图,并由该图建立最小生成树。
\(q\) 次询问,每次询问最小生成树上两点间最大边权。
\(n\le 10^5; m\le 5\times 10^5; q\le 10^7\)
三倍经验:

思路:

仔细分析题意后不难发现所求即是原图所对应 Kruskal 重构树中两点 LCA 的权值。注意到 \(q\le 10^7\) 因此使用倍增或树剖求 LCA 容易 TLE,需要用 Tarjan 离线求 LCA。
时间复杂度 \(\mathcal O(m\log m+n\cdot\alpha(n)+n+q)\)

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
#include<numeric>
#include<algorithm>
using uint64=unsigned long long;
inline uint64 getint() {
	char ch;
	while(!isdigit(ch=getchar()));
	uint64 x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
typedef unsigned long long ull;
ull myRand(ull &k1, ull &k2){
    ull k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= (k3 <<23);
    k2 = k3 ^ k4 ^ (k3 >>17) ^ (k4 >>26);
    return k2 + k4;
}
std::pair<int,int>myRanq(ull&k1,ull&k2,int MAXN){
    int x=myRand(k1,k2)%MAXN+1,y=myRand(k1,k2)%MAXN+1;
    if(x>y)return std::make_pair(y,x);
    else return std::make_pair(x,y);
}
constexpr int N=2e5+1,logN=18,M=5e5+1;
struct Edge {
	int u,v,w;
	bool operator < (const Edge &rhs) const {
		return w<rhs.w;
	}
};
Edge edge[M];
int n,m;
int val[N];
int par[N],dep[N]={-1},anc[N][logN];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
	e[u].emplace_back(v);
	anc[v][0]=u;
}
struct DisjointSet {
	int anc[N];
	int find(const int &x) {
		return x==anc[x]?x:anc[x]=find(anc[x]);
	}
	inline void init(const int &n) {
		std::iota(&anc[1],&anc[n]+1,1);
	}
	inline bool same(const int &x,const int &y) {
		return find(x)==find(y);
	}
	inline void merge(const int &x,const int &y) {
		anc[find(x)]=find(y);
	}
};
DisjointSet djs;
std::vector<int> q[N];
bool vis[N];
int ans=0;
void tarjan(const int &x) {
	for(int &y:e[x]) {
		tarjan(y);
		djs.merge(y,x);
	}
	for(int &y:q[x]) {
		if(!vis[y]) continue;
		ans^=val[djs.find(y)];
	}
	vis[x]=true;
}
int main() {
	n=getint(),m=getint();
	for(int i=1;i<=m;i++) {
		edge[i].u=getint();
		edge[i].v=getint();
		edge[i].w=getint();
	}
	std::sort(&edge[1],&edge[m]+1);
	djs.init(n*2-1);
	int t=n;
	for(int i=1;i<=m;i++) {
		const int u=djs.find(edge[i].u);
		const int v=djs.find(edge[i].v);
		const int &w=edge[i].w;
		if(u==v) continue;
		val[++t]=w;
		add_edge(t,u);
		add_edge(t,v);
		djs.merge(u,t);
		djs.merge(v,t);
	}
	const int q=getint();
	uint64 a1=getint(),a2=getint();
	for(int i=0;i<q;i++) {
		const auto q=myRanq(a1,a2,n);
		::q[q.first].emplace_back(q.second);
		::q[q.second].emplace_back(q.first);
	}
	djs.init(t);
	tarjan(t);
	printf("%d\n",ans);
}
posted @ 2021-09-11 12:05  skylee03  阅读(84)  评论(0编辑  收藏  举报