2018杭电多校第一场

感觉最近被茫茫的前路以及紧迫的时间弄得有点乱了阵脚,calm,calm,脚踏实地地一步步往前走吧

B

实验室环境好差,感觉都要变成游戏厅了。搞不懂,明明在家里玩更舒服,干嘛非要留校呢。

 

这道题,显而易见的一点是:对于一个字符串,如果它内部已经有合法的括号序列,那么不管怎么进行重排,这些序列是不会受到影响的,也就是说,可以预处理掉这些序列,然后得到的字符串一定都是a个')' 加 b个‘(’的形式(a, b >= 0)。

然后就是本题的精髓了,如何安排这些序列?

考虑x,y两个序列,它们的安排方式有且仅有两种——x在y的前面或者y在x的前面,它们消掉括号的数目分别为 aa = min(x. b, y. a), bb = min(x.a, y. b),那么显然,x,y的先后取决于aa,bb孰大孰小。相等的时候怎么办?答:左括号多的排在前面。理由如下:

分析一下可以知道,aa=bb只有两种情况

情况1:

右括号数目大于左括号,且左括号数目相等,此时,由于左括号数目相等,所以排序策略不会产生影响,是正确的

情况2:

右括号数目小于左括号,且右括号数目相等,此时,左括号数目越多,则越容易产生出合法序列。

ps:另一个小收获,以后做括号匹配是不需要写stack的,数组模拟下就好

#include 
#include 
#include 
#include 
using namespace std;

const int N = 100100;

char s[N];
struct Node{
	int l, r, sum;
}node[N];

bool cmp(Node x, Node y){
	int aa = min(x. l, y. r);
	int bb = min(x. r, y. l);
	if(aa == bb){
		return x. l > y. l;
	}
	return aa > bb;
}

Node get_val(){
	int lens = strlen(s);
	Node res;
	res. l = 0, res. r = 0, res. sum = 0;
	for(int i = 0; i < lens; i ++){
		if(s[i] == ')'){
			if(res. l > 0){
				res. l --;
				res. sum ++;
			}
			else{
				res. r ++;
			}
		}
		else{
			res. l ++;
		}
	}
	return res;
}

int main(){
	int T;
	scanf("%d", &T);
	while(T --){
		int n;
		scanf("%d", &n);
		for(int i = 0; i < n; i ++){
			scanf("%s", s);
			node[i] = get_val();
		}
		sort(node, node + n, cmp);
		Node nowl, nxt, tmp;
		nowl = tmp = node[0];
		for(int i = 1; i < n; i ++){
			nxt = node[i];
			tmp. l = nowl. l + nxt. l - min(nowl. l, nxt. r);
			tmp. r = nowl. r + nxt. r - min(nowl. l, nxt. r);
			tmp. sum = nowl. sum + nxt. sum + min(nowl. l, nxt. r);
			nowl = tmp;
		}
		printf("%d\n", nowl. sum * 2);
	}
	return 0;
}

 

D

一开始写了个set,超时了,感觉这题有种用小根堆的意思,但是不知道该怎么用

R[l]:每一个l所对应的r

然后从1扫到n,last代表上一个区间的起点,nowl代表在该位置之前(包括该位置)的数字已经填好。剩下的就是一个模拟的过程。至于这个复杂度。。玄学。现在敲代码会犯一些很愚蠢的错误,改。

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int N = 100100;

int a[N];
int R[N];
int n, m;
priority_queue , greater > pq;

int main(){
	int T;
	scanf("%d", &T);
	while(T --){
		scanf("%d %d", &n, &m);
		while(! pq. empty()){
			pq. pop();
		}
		for(int i = 1; i <= n; i ++){
			pq. push(i);
			R[i] = i;
		}
		int x, y;
		for(int i = 1; i <= m; i ++){
			scanf("%d %d", &x, &y);
			R[x] = max(R[x], y); 
		} 
		int last = 1, nowl = 0;
		for(int i = 1; i <= n; i ++){
			if(nowl >= R[i]){
				continue;
			}
			while(last < i){
				pq. push(a[last ++]);
			}
			while(nowl < R[i]){
				a[nowl + 1] = pq. top();
				pq. pop();
				nowl ++;
			}
		}
		for(int i = 1; i <= n; i ++)
			printf("%d%c", a[i], i == n ? '\n' : ' ');
	}
	return 0;
} 

 

G

本质上和A一样,找规律,毕竟咱不会证明。。

可是即使是找规律,还是有种啃不动的感觉,对着整整一张表的数据茫然发呆

看了大佬的题解才知道应该怎么找这个规律:找每个数出现次数和n的关系,然后出现次数相同的数构成一个等差数列,用公式求和即可。其中还有各种各样的玄学操作,我只能把它认为是巧合??就像重复次数等于末尾1的位置。

然后是这道题的代码,感觉各种玄学,就像是在拼凑一样,emmm,话说找规律本身也是一种玄学吧

#include
using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;
const ll inv2 = 500000004;

ll pw[80]; //pw[i]=2^i
ll cnt[80];//cnt[i]=2^(i+1)-1
ll n, pos;

void init(){ 
    pw[0] = cnt[0] = 1;
    for(int i = 1;i <= 62; i ++){   //足够涵盖所有数据 
        pw[i] = pw[i - 1] * 2;
        cnt[i] = 2 * pw[i] - 1;
    }
}

ll getsum(ll p){//计算序列中出现的所有1,2...p的和 
    ll ans = 0;
    for(ll i = 1; i <= p; i *= 2){
        ll num = (p - i) / (2 * i); //首项=i, num=项数-1 
        ll last = i + num * (2 * i);//公差是2*i, 算出末项 

        num = (num + 1) % mod;

        ll tmp = (i + last) % mod;//等差数列求和S=(首项+末项)*项数/2 
        tmp = tmp * num % mod;
        tmp = tmp * inv2 % mod;
        tmp = tmp * (__builtin_ffsll(i)) % mod;//乘以对应的出现次数,右边起第一个1的位置 
        ans = (ans + tmp) % mod; 
    }
    return (1 + ans) % mod;//加上第一项被忽略的1 
}

int main(){
    init();
    int T;
    scanf("%d", &T);
    while(T --){
        scanf("%lld", &n);
        n --;
        if(n == 0){
			puts("1");
			continue;
		}
        pos = 0;
        ll tmp = n;
        //在log的时间内求出pos
        for(int i = 62; i >= 0; i --){
            if(tmp >= cnt[i]){
                tmp -= cnt[i];
                pos += pw[i];
            }
        }

        ll ans = getsum(pos);
        if(tmp)
			ans = (ans + tmp % mod * (pos + 1) % mod) % mod;//如果有剩下的几项,就再加上,  下一个数一定比pos大1,且一共有tmp个这样的数 
        printf("%lld\n", ans);
    }
    return 0;
}

 

H

刚开始补题:这题一定可以挖出很多知识,好好看好好学

a few moment later ...   :dls板子真好用

具体思路还是看dls直播吧,我只是注释了下代码。听起来感觉dls的做法很有道理,但是原因又想不明白,单纯是结点的概率乘积再乘[0,1]的期望为什么就是答案都半知半解。。。

#include 
using namespace std;

typedef long long ll;

const int N = 1000100;
const ll mod = 1000000007;

ll ans;
ll inv[N];

struct Tree {
    int root, top, n;
    int sta[N], l[N], r[N];
    bool vis[N];  //用来找根节点 
    void build(int *num, int nn) {
        n = nn;
        top = 0;
        memset(l, 0, sizeof(int) * (n + 1));
        memset(r, 0, sizeof(int) * (n + 1));
        memset(vis, 0, sizeof(bool) * (n + 1));
        for(int i = 1; i <= n; i ++) {
            int tmp = top;
            //沿最右端的这条链,按从叶子到根的顺序寻找第一个小于i的位置 
            while(top > 0 && num[sta[top - 1]] < num[i]) {
                top --;
            }
            if(top != 0) {
                r[sta[top - 1]] = i;
            }
            if(top < tmp) {
                l[i] = sta[top];
            }
            sta[top ++] = i;
        }
        //如果一个结点不是任何一个结点的孩子,那么它就是根节点 
        for(int i = 1; i <= n; i ++) {
            vis[l[i]] = vis[r[i]] = true;
        }
        for(int i = 1; i <= n; i ++) {
            if(! vis[i]) {
                root = i;
            }
        }
    }
    int dfs(int x) {
    	//如果这棵树有(加上根)有cnt个结点,那么根节点是最大值的概率就是1/cnt 
        int cnt = 1;
        if(l[x] != 0) {
            cnt += dfs(l[x]);
        }
        if(r[x] != 0) {
            cnt += dfs(r[x]);
        }
        ans = (ans * inv[cnt]) % mod;
        return cnt;
    }
};
int T, n;
int num[N];
Tree t;
//线性时间求逆元
void Init() { 
    inv[1] = 1;
    for(int i = 2; i < N; ++i) {
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}
int main() {

    Init();
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        ans = (n * inv[2]) % mod;   //均匀分布的概率密度 
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &num[i]);
        }
        t. build(num, n);
        t. dfs(t. root);
        printf("%I64d\n", ans);
    }
    return 0;
}

 

 

你可能感兴趣的:(2018杭电多校第一场)