zoukankan      html  css  js  c++  java
  • 牛客练习赛88游记

    牛客练习赛88游记

    A - 活着的证据

    题目大意:

    将一个各数位都为 (1sim 8) 的数逐位写成罗马数字,可以得到一个仅包含字符 VI 的串。
    其中阿拉伯数字 (1sim 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 Nle 5 imes 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 imes 10^5; 1le Kle 6)

    思路:

    基础的字符串哈希题。
    显然若 (|M| e |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) 位二进制数。在相邻两数之间插入异或、同或运算符,使得最终结果最大。
    (nle 10^6; kle 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
    ",std::max(ans,(1llu<<k)-1-ans));
    	} else {
    		printf("%llu
    ",std::max(ans,~ans));
    	}
    }
    

    D - 都市的柏油路太硬

    题目大意:

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

    思路:

    仔细分析题意后不难发现所求即是原图所对应 Kruskal 重构树中两点 LCA 的权值。注意到 (qle 10^7) 因此使用倍增或树剖求 LCA 容易 TLE,需要用 Tarjan 离线求 LCA。
    时间复杂度 (mathcal O(mlog m+ncdotalpha(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
    ",ans);
    }
    
  • 相关阅读:
    ld: symbol(s) not found for architecture arm64
    一个好玩的命令
    统计代码行数命令
    【转】 C语言自增自减运算符深入剖析
    gcc 生成动态库时-fpic选项是什么意思。
    每天一条linux命令——halt
    每天一条linux命令——shutdown
    推荐一些编程学习的网站
    每天一条linux命令——login
    linux如何开机以命令行形式启动?
  • 原文地址:https://www.cnblogs.com/skylee03/p/15254260.html
Copyright © 2011-2022 走看看