2019 计蒜之道 复赛

29 篇文章 0 订阅
4 篇文章 0 订阅

前言


貌似gou到了一件衣服(小声bibi)

一年前:相同的6.17  计蒜之道2018
一年的变化可真大,然鹅我还是那么菜
233333
___
初赛xjb AK了一场就进了复赛(其实人均AK,只是手速快一点)

 大概打的是第3场

看吧,人均AK

 

然后冒着上学迟到的风险打了复赛


打开题目,WTF 4小时7题,开第一题 A https://nanti.jisuanke.com/t/39611

emmmmm……MD什么鬼畜题面

就是先把最长上升子序列的个数求出来,就是q

然后对于每个位置i 求经过i的最长上升子序列的个数p,然后输出p/q

第一眼XJB线段树

开一颗权值线段树,维护最大值和最大值的个数。

设f[i]为以i结尾的最长上升子序列长度,ff[i]为以i结尾的最长上升子序列的个数

然后线段树优化转移

求出最长上升子序列的个数设为q,最长上升子序列长度设为k

然后从后往前跑一边最长下降子序列

同理设为g[i]和gg[i]

然后如果f[i]+g[i] == k + 1,那进过i点的最长上升子序列个数就是ff[i]*gg[i]个

最后就……没了

时间复杂度 O(n log n)

写完交上去

WA

一整凉意心头来

切回去看看好多人切了D题

什么水题

码码码 T一发之后就切了

回到A题

经过一发思索,发现

我汤姆(WTM)没膜

%一发后就A了

考场代码:

#include<bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define int long long
#define mod 998244353
#define N 4000005
using namespace std;
int ma[N], ma_[N], f[N], ff[N], a[N], b[N], g[N], gg[N], n;
void update(int rt){//ma 表示最长上升子序列的长度(最大值)  ma_表示最长上升子序列个数(最大值的个数) 
	if(ma[ls] > ma[rs]) ma[rt] = ma[ls], ma_[rt] = ma_[ls];
	if(ma[ls] < ma[rs]) ma[rt] = ma[rs], ma_[rt] = ma_[rs];
	if(ma[ls] == ma[rs]) ma[rt] = ma[ls], ma_[rt] = ma_[ls] + ma_[rs];
	ma_[rt] %= mod;
}
void build(int l, int r, int rt){
	if(l == r) return;
	ma[rt] = ma_[rt] = 0; 
	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r, rs);
	update(rt);
}
void insert(int l, int r, int x, int o1, int o2, int rt){
	if(l == r){
		if(ma[rt] == o1) ma_[rt] += o2; else ma_[rt] = o2;
	    ma_[rt] %= mod;
		ma[rt] = o1;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) insert(l, mid, x, o1, o2, ls);
	else insert(mid + 1, r, x, o1, o2, rs);
	update(rt);
}
int query1(int l, int r, int L, int R, int rt){//求最大值 
	if(L <= l && r <= R) return ma[rt];
	int mid = (l + r) >> 1, ret = 0;
	if(L <= mid) ret = query1(l, mid, L, R, ls);
	if(R > mid) ret = max(ret, query1(mid + 1, r, L, R, rs));
	return ret;
}
int query2(int l, int r, int L, int R, int rt, int ha){//求最大值的个数 
	if(L <= l && r <= R){
		if(ma[rt] == ha) return ma_[rt];//注意要等于最大值才能返回 
		return 0;
	}
	int mid = (l + r) >> 1, ret = 0;
	if(L <= mid) ret = query2(l, mid, L, R, ls, ha);
	if(R > mid) ret += query2(mid + 1, r, L, R, rs, ha), ret %= mod;
	return ret;
}
int qpow(int y, int x){//卡速米  
	int t = y, ans = 1;
	for(;x; x >>= 1, t = t * t % mod) if(x & 1) ans = ans * t % mod;
	return ans;
}
signed main(){ 
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), b[i] = a[i];
	sort(b + 1, b + 1 + n);
	for(int i = 1; i <= n; i ++) a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b; //离散化 
	//for(int i = 1; i <= n; i ++) printf("%lld ", a[i]); printf("\n");
	build(0, n, 1);
	for(int i = 1; i <= n; i ++){
		f[i] = query1(0, n, 0, a[i] - 1, 1) + 1;//求小于a[i]当中f值最大的数 + 1就是以i结尾的最长上升子序列 
		ff[i] = query2(0, n, 0, a[i] - 1, 1, f[i] - 1);//同上,最大值的个数就是以i结尾的最长上升子序列的个数 
		ff[i] = max(ff[i], 1ll);//注意长度和个数至少为1 
		insert(0, n, a[i], f[i], ff[i], 1);//插入♂ 
	}
//	for(int i = 1; i <= n; i ++) printf("%lld ", f[i]); printf("\n"); 考场上的xjb调试 
//	for(int i = 1; i <= n; i ++) printf("%lld ", ff[i]); printf("\n");
	int qq = qpow(ma_[1], mod - 2), pp = ma[1];
//	printf("**%lld** ", qq);
	memset(ma, 0, sizeof ma);
	memset(ma_, 0, sizeof ma_);
	build(0, n + 1, 1);
	for(int i = n; i >= 1; i --){
		g[i] = query1(0, n + 1, a[i] + 1, n + 1, 1) + 1;//同上注意边界 
		gg[i] = query2(0, n + 1, a[i] + 1, n + 1, 1, g[i] - 1);
		gg[i] = max(gg[i], 1ll);
		insert(0, n + 1, a[i], g[i], gg[i], 1);
	}
	
//	for(int i = 1; i <= n; i ++) printf("%lld ", g[i]); printf("\n");
//	for(int i = 1; i <= n; i ++) printf("%lld ", gg[i]); printf("\n");
	
	
	for(int i = 1; i <= n; i ++){
		if(f[i] + g[i] - 1 == pp) printf("%lld ", ff[i] * gg[i] % mod * qq % mod); else printf("0 ");//xjb输出 
	}
	return 0;
}

然后一个半小时过去了

官方题解

看见没,和我的一样23333333

 


D题 “星云系统”   https://nanti.jisuanke.com/t/39614

什么鬼畜名字

 

 

这题面……我给满分!

 

就是对于26个字母每个字母把它出现的位置记下来然后贪心就完了

听说叫做序列自动机

一开始用二分T了一波,改一下就A了

时间复杂度  O(26n)

#include<bits/stdc++.h>
using namespace std;
int n, len, pos[28];
vector<int> a[28];
char st[5000005];
int find(int x, int y){//因为位置只增不减,所以每个字母维护一个 pos就可以了 
	while(a[x][pos[x]] < y && pos[x] <= a[x].size() - 1){
		pos[x] ++;
	}
	return a[x][pos[x]];
}
void dfs(int x, int y){
	if(!y) return;
	for(int i = 0; i < 26; i ++){//贪心找 
		int k = find(i, x);
		if(k >= x && len - k >= y - 1){//如果能塞就塞 
			printf("%c", char(i + 'a'));
			dfs(k + 1, y - 1);
			return;
		}
	}
}
int main(){
	for(int i = 0; i < 26; i ++) a[i].push_back(0);
	scanf("%s", st + 1);
	len = strlen(st + 1);
	for(int i = 1; i <= len; i ++){
		a[st[i] - 'a'].push_back(i);//把每个字母出现的位置记下来 
	}
	scanf("%d", &n);
	dfs(1, n);
	return 0;
}

是不是很短,是不是很简单23333

 

官方题解

妙啊!


然后刚了一个小时的Bhttps://nanti.jisuanke.com/t/39612

题面自己去看吧,不难理解

这题就是贪心

枚举加哪张牌,然后在枚举哪个做对子

主要烦的是那些出现次数>=3的牌,要不要作为刻子

尝试了很多种贪心

后来一怒之下二进制枚举

然后就A 了

23333

时间复杂度 O(1)  都是常数,没毛病

考场代码:  不解释了,又不难,为了避免阅读代码时的不适我把考场的调试删了

#include<bits/stdc++.h>
using namespace std;
int c[55], a[55], b[55], A[55], dd[55];
int check(){
	for(int pos = 1; pos <= 34; pos ++) if(a[pos] >= 2){
		for(int i = 1; i <= 34; i ++) A[i] = a[i];
		A[pos] -= 2;
		
		int sz = 0;
		for(int i = 1; i <= 34; i ++) if(A[i] >= 3) dd[++ sz] = i;
		
		for(int S = 0; S < (1 << sz); S ++){
			for(int i = 1; i <= 34; i ++) c[i] = A[i];
			for(int i = 1; i <= sz; i ++)
				if((1 << (i - 1)) & S) c[dd[i]] -= 3;

			for(int i = 1; i <= 7; i ++)
				if(c[i + 1] >= c[i] && c[i + 2] >= c[i]) c[i + 1] -= c[i], c[i + 2] -= c[i], c[i] = 0;	
			for(int i = 10; i <= 16; i ++)
				if(c[i + 1] >= c[i] && c[i + 2] >= c[i]) c[i + 1] -= c[i], c[i + 2] -= c[i], c[i] = 0;
			for(int i = 19; i <= 25; i ++)
				if(c[i + 1] >= c[i] && c[i + 2] >= c[i]) c[i + 1] -= c[i], c[i + 2] -= c[i], c[i] = 0;
				
			int f = 0;
			for(int i = 1; i <= 34; i ++) if(c[i]) f = 1;
			if(!f) return 1;
		}
	}
	return 0;
}
int x;
char cc;
int main(){
	while(~scanf("%d%c", &x, &cc)){
		memset(b, 0, sizeof b);
		memset(a, 0, sizeof a);
		memset(dd, 0, sizeof dd);
		memset(c, 0, sizeof c);
		memset(A, 0, sizeof A);
			if(cc == 'm') b[x] ++;
			if(cc == 's') b[x + 9] ++;
			if(cc == 'p') b[x + 9 + 9] ++;
			if(cc == 'z') b[x + 9 + 9 + 9] ++;
		for(int i = 2; i <= 13; i ++){
			scanf(" %d%c", &x, &cc);
			if(cc == 'm') b[x] ++;
			if(cc == 's') b[x + 9] ++;
			if(cc == 'p') b[x + 9 + 9] ++;
			if(cc == 'z') b[x + 9 + 9 + 9] ++;
		}
		for(int i = 1; i <= 34; i ++) if(b[i] < 4){
			for(int j = 1; j <= 34; j ++) a[j] = b[j];
			a[i] ++;
			if(check()){
				if(i <= 9) printf("%dm\n", i);
				else
				if(i <= 18) printf("%ds\n", i - 9);
				else
				if(i <= 27) printf("%dp\n", i - 18);
				else printf("%dz\n", i - 27);
			}
		}	
	}
	
	return 0;
}

诡异的运行时间

官方题解


看了看E题

一看钟,快五点了

赶紧拎着书包就去上学了

咕咕咕咕

其他题:待填坑~

 

E 撑起信息安全“保护伞”  https://nanti.jisuanke.com/t/39615

WTM 暴力

不解释了,看代码

code:

#include<bits/stdc++.h>
using namespace std;
int n;
char st[1000005], stt[1000005];
int check(){
	int ret = 0;
	for(int i = 1; i <= n; i ++){
		if(stt[i] == '(') ret ++;
		else ret --;
		if(ret < 0) return 0;
	}
	return !ret;
}
int main(){
	scanf("%s", st + 1);
	n = strlen(st + 1);
	for(int i = 1; i <= n; i ++) stt[i] = st[i];
	while(1){
		prev_permutation(stt + 1, stt + 1 + n);
		if(check()) break;
	}
	for(int i = 1; i <= n; i ++) printf("%c", stt[i]); printf("\n");
	
	for(int i = 1; i <= n; i ++) stt[i] = st[i];
	
	while(1){
		next_permutation(stt + 1, stt + 1 + n);
		if(check()) break;
	}
	for(int i = 1; i <= n; i ++) printf("%c", stt[i]); printf("\n");
	
	return 0;
} 

 

官方题解

暴力你就win了,不过这个也挺有意思的


 

 

update:

收到衣服啦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值