2018杭电多校第一场

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

A

这种数学题是我的弱项,今天看到这道题也是感觉无从下手,到最后抱着写几项看看的想法竟然误打误撞发现了规律。。

如果可以整除3,那么答案就是 (n/3)*(n/3)*(n/3),不能整除三时如果可以整除4,那么答案就是 (n/4) * (n/4) *(n/2),其余情况都是-1.

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int N = 1e6 + 10;
typedef long long ll;

bool is_prime[N];

int main(){
    int t;
    scanf("%d", &t);
    while(t --){
        ll n;
        scanf("%lld", &n);
        if(n % 3 == 0){
            printf("%lld\n", (n / 3) * (n / 3) * (n / 3));
            continue;
        }
        if(n % 4 == 0){
            printf("%lld\n", (n / 2) * (n / 4) * (n / 4));
            continue;
        }
        printf("-1\n");
    }
    return 0;
}

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 <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
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;
}

C

按横坐标排个序就可以了~~~

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 10010;

struct Node{
	int x, y, id;
}node[3* N];

bool cmp(Node a, Node b){
	return a. x < b. x;
}

int main(){
	int t;
	scanf("%d", &t);
	while(t --){
		int n;
		scanf("%d", &n);
		for(int i = 1; i <= 3 * n; i ++){
			scanf("%d %d", &node[i]. x, &node[i]. y);
			node[i]. id = i;
		}
		sort(node + 1, node + 3 * n + 1, cmp);
		for(int i = 1; i <= 3 * n; i ++){
			printf("%d%c", node[i]. id, i % 3 == 0 ? '\n' : ' ');
		}
	}
	return 0;
}

D

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

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

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

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 100100;

int a[N];
int R[N];
int n, m;
priority_queue <int, vector<int>, greater<int> > 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的位置。

说一个最直接的收获——我终于知道除法怎么取模了,先贴个求逆元的模板

//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
    if(a==0&&b==0) return -1;//无最大公约数
    if(b==0){x=1;y=0;return a;}
    long long d=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
    long long x,y;
    long long d=extend_gcd(a,n,x,y);
    if(d==1) return (x%n+n)%n;
    else return -1;
}

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

#include<bits/stdc++.h>
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 <bits/stdc++.h>
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;
}

K

当时做的时候真的宛若智障,借位这件事都忘了,呼。。找回状态

这题注意下浮点误差就好了,只要计算机的核心结构不变,浮点误差是不可能从根本上解决的,只能曲线救国,因地制宜,像这道题,小数的位数是明确的,那就好说了,不管三七二十一,见面先*10

#include <iostream>
#include <cmath>
using namespace std;

int main(){
    int t;
    while(cin >> t){
        while(t --){
            int a, b;
            char c;
            double d;
            scanf("%d %d UTC%lf", &a, &b, &d);
            a = (a - 8 + 24) % 24;
            int ans1, ans2;
            int dd = d * 10;
            int temp = dd * 6;
            int t1 = (temp + b) / 60;
            int t2 = (temp + b) % 60;
    //3        cout << temp << ' ' <<  t1 << ' ' << t2 << endl;
            if(t2 < 0)
                t1 --;
            ans1 = (a + t1 + 24) % 24;
            ans2 = (t2 + 60) % 60;
            printf("%02d:%02d\n", ans1, ans2);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38759433/article/details/81603291