牛客寒假集训营补题

小沙的构造

利用"!’*±.08:=^_WTYUIOAHXVM|<>/[]{}()构造中心对称的字符串。
情况超级多,比如,<[{(这五个字符就是中心对称的,其他的都是轴对称。
不想把代码写成屎山,原因是一直分类讨论各种情况下该用多少中心对称字符,多少轴对称字符。
其实,只要从0~5枚举中心对称字符种类,那么剩余种类都是轴对称字符,再判断此时能不能构造出长度为N的字符串,就可以了。这样子就避免了各种分类讨论。
注意0到5都要试一遍,不行的话continue,不能return

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 1000000007;
map<char, char> mp;
char f(char k)
{
   
    if (mp.count(k)) return mp[k];
    else return k;
}
int check(int n, int m)
{
   
    for (int i = 0; i <= 5; i++){
   
        int t = m - i * 2;
        if (t < 0 || t > 25) continue;
        int mi = i * 2 + t * 2;
        if (n & 1 && t > 0) mi--;
        if (mi <= n) return i;
    }
    return -1;
}
signed main()
{
   
    int n, m;
    cin >> n >> m;
    // cout << check(n, m) << endl;
    string t = "\"!'*+-.08:=^_WTYUIOAHXVM|"; //25
    string s = "<\\[{("; //5
    mp['<'] = '>', mp['\\'] = '/', mp['['] = ']', mp['{'] = '}', mp['('] = ')';
    string ans;
    int k = check(n, m);
    if (k == -1) {
   
        puts("-1");
        return 0;
    }
    m -= k * 2;
    for (int i = 0; i < k; i++) ans += s[i];
    for (int i = 0; i < m; i++) ans += t[i];
    int len = ans.length();
    if (n & 1) {
   
        int l = n - (len * 2 - 1);
        for (int i = 1; i <= l; i++) ans += f(ans[len - 1]);
        for (int i = 1, j = len - 2; i <= len - 1; i++, j--) ans += f(ans[j]);
    }
    else {
   
        int l = n - len * 2;
        for (int i = 1; i <= l; i++) ans += f(ans[len - 1]);
        for (int i = 1, j = len - 1; i <= len; i++, j--) ans += f(ans[j]);
    }
    cout << ans;
    return 0;
}

小沙的remake

本质上是求区间上身子序列有多少个(单个的数字也算)。
思路就是以当前的数字结尾,求有多少个合法的子序列。
如果去掉区间的限制,那么直接树状数组+离散化就可以做到了。

做法一:
类似于牛牛种花这题,排序完从递增的x轴开始,y轴设置成树状数组,这样就能查询一个坐标左下角有多少朵花了。本题就相当于,x固定,直线y到直线y-b之间有多少朵花。
先把数组a放进结构体数组中,结构体还包含下标pos,排完序后,开始放进树状数组,最后求和即可。pos相当于种花的y轴。

做法二:
如果无区间的限制,直接在树状数组上找比自己小的数,然后求和再继承就行。有区间的限制,可以这样想,在历史上比a[i]小的数的和提前求好,遇到a[i]时再求一次比a[i]小的数的和,相减一下就可以达到求区间里比a[i]小的数的和了。
用vector来存历史上的那些求和结果比较的方便。
做法二代码:代码查看

const int N = 2e6 + 7, mod = 1e9 + 7;
int a[N], b[N];
int c[N];
struct node{
   
	int a, pos;
	bool operator < (const node &k) const {
   
		if (a != k.a) return a < k.a;
		return pos < k.pos;
	}
} d[N];
signed main()
{
   
	for (int i = 1; i <= n; i++)
	{
   
		d[i].a = a[i];
		d[i].pos = i;
	}
	sort(d + 1, d + 1 + n);
	for (int i = 1; i <= n; i++){
   
		int pos = d[i].pos;
		int sum = (que(pos) - que(pos - b[pos] - 1) % mod + mod) % mod;
		add(pos, sum + 1);
	}
	cout << (que(n) + mod) % mod;
	return 0;
}

小沙的魔法

给定m条可以使用的边和n个城市。想让每个城市高度涨到a[i],每次操作可以给城市高度+1,若给两个城市连接,那么两个城市合并成一个。求最小长高的操作次数。
思路:反过来想,如果两个城市的高度达到一致了,那么两个城市就连个边,就可以一起降低到0。那么对于m条边,边权赋为两点的点权的最小值,然后排个序,像最小生成树一样做就好了。

#include <bits/stdc++.h>
using namespace std
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值