zoukankan      html  css  js  c++  java
  • 8.1集训模拟赛题解

    A.折纸

    题目描述

    小s很喜欢折纸。
    有一天,他得到了一条很长的纸带,他把它从左向右均匀划分为(n)个单位长度,并且在每份的边界处分别标上数字(1-n)
    然后小s开始无聊的折纸,每次他都会选择一个数字,把纸带沿这个数字当前所在的位置翻折(假如已经在边界上了那就相当于什么都不做)。
    小s想知道 次翻折之后纸带还有多长。

    输入格式

    输入包含多组数据,第一为一个正整数(T)
    接下来,每组数据包括两行。
    第一行包含两个正整数(n)(m),表示纸带的长度和操作的次数。
    第二行包含(m)个整数(Di),其中(Di)表示第(i)次选择的数字。

    输出格式

    每组数据输出一行,只有一个数字,即纸带最后的长度。

    样例

    (input)

    2
    5 2
    3 5
    5 2
    3 2
    

    (output)

    2
    2
    

    思路

    思路1(并查集模拟)

    把折叠后重合的点连起来,最后看有多少个集合并减1(因为是区间),得到答案。

    #include <cstdio>
    #include <cstring>
    #include <iostream>  //long long
    #include <algorithm>
    using namespace std;
    const int maxn = 3e3 + 5, INF = 0x3f3f3f3f;
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-')
                w = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
        return s * w;
    }
    int n, m, f[10000005];
    void Init() {
        for (int i = 1; i <= n; i++) f[i] = i;
    }
    int Find(int x) {
        if (f[x] == x)
            return x;
        return f[x] = Find(f[x]);
    }
    void Merge(int a, int b) { f[Find(b)] = Find(a); }
    int main() {
        int T = read();
        while (T--) {
            n = read(), m = read();
            Init();
            int l = 0, r = n;
            for (int i = 1, now; i <= m; i++) {
                now = read();
                now = Find(now);
                if (now > r)
                    now = r * 2 - now;
                else if (now < l)
                    now = l * 2 - now;
                if (now - l > r - now) {
                    for (int j = now + 1; j <= r; j++) Merge(now * 2 - j, j);
                    r = now;
                } else {
                    for (int j = l; j <= now - 1; j++) Merge(now * 2 - j, j);
                    l = now;
                }
                // cout<<l<<" "<<r<<" "<<endl;
            }
            cout << r - l << endl;
        }
        return 0;
    }
    

    思路2(正解,维护左右端点)

    由于每次折叠都是由前一个折叠后的状态转移过来的,所以我们将每次折叠实际位置维护起来,不断更新,要是统一都向右折,根据数据范围显然会炸long long,必须要开unsigned long long,可以都向中间折叠,可以不用开unsigned long long,下代码并没有向中间折叠,实际上方法是一样的。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define rint register int
    using namespace std;
    const int maxn = 3050;
    inline unsigned long long read(){
    	unsigned long long x = 0, f = 1; char ch = getchar();
    	for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    	return x * f;
    }
    int t, m;
    unsigned long long n, sol[maxn];
    int main(){
    	t = read();
    	while(t--){
    		n = read(), m = read();
    		unsigned long long l = 0, r = n;
    		for(rint i = 1; i <= m; i++){
    			sol[i] = read();
    			for(rint j = 1; j < i; j++){
    				if(sol[j] <= sol[i]) continue;
    				else{
    					sol[i] = sol[j] * 2 - sol[i];
    				}
    			}
    			r = max(sol[i] * 2 - l, r);
    			l = sol[i];
    		}
    		printf("%lld
    ", r - l);
    	}
    	return 0;
    }
    
    

    B.water(来源POI1999,略有不同)

    题目描述

    有一块矩形土地被划分成(n imes m)个正方形小块。这些小块高低不平,每一小块都有自己的高度。水流可以由任意一块地流向周围四个方向的四块地中,但是不能直接流入对角相连的小块中。
    一场大雨后,由于地势高低不同,许多地方都积存了不少降水。给定每个小块的高度,求每个小块的积水高度。
    注意:假设矩形地外围无限大且高度为0。

    输入格式

    第一行包含两个非负整数n,m。
    接下来n行每行m个整数表示第i行第j列的小块的高度。

    输出格式

    输出n行,每行m个由空格隔开的非负整数,表示每个小块的积水高度。

    样例

    (input)

    3 3
    4 4 0
    2 1 3
    3 3 -1
    

    (output)

    0 0 0
    0 1 0
    0 0 1
    

    思路

    本题目有两种解法,一种是用朴素的宽搜解决问题(考试的时候时间不够懒得写了),另一种是gyz大佬想出来的kruskal的方法(挺难想到的),orz。

    思路1

    外围高度为0,所以高度为负的至少能够装水到地平,首先能确定储水量的是矩形的四边上的点,对于其他的点就不好直接判断了,但根据水桶原理,能储多少水取决于最短的那块木板,所以我们可以把四边上的每一个点放到一个小根堆里,依次从高度最低的点往里搜,显然比他高的点是存不了水的,比他低的点可以存储他们高度差的水量,依次搜索即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int maxn = 300 + 5, INF = 0x3f3f3f3f, kx[4] = { 0, 1, 0, -1 }, ky[4] = { 1, 0, -1, 0 };
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-')
                w = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
        return s * w;
    }
    struct Node {
        int x, y, w;
        friend bool operator<(const Node &A, const Node &B) { return A.w > B.w; }
    };
    int n, m, a[maxn][maxn], h[maxn][maxn], black[maxn][maxn];
    priority_queue<Node> q;
    int main() {
        n = read(), m = read();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) h[i][j] = a[i][j] = read();
        for (int i = 0; i <= n + 1; i++) q.push((Node){ 0, i, 0 });
        for (int i = 0; i <= m + 1; i++) q.push((Node){ i, 0, 0 });
        while (!q.empty()) {
            Node now = q.top();
            q.pop();
            int x = now.x, y = now.y, w = now.w;
            for (int i = 0; i < 4; i++) {
                int nx = x + kx[i], ny = y + ky[i];
                if (nx < 0 || ny < 0 || nx > n + 1 || ny > m + 1 || black[nx][ny])
                    continue;
                h[nx][ny] = max(h[nx][ny], w);
                black[nx][ny] = 1;
                q.push((Node){ nx, ny, h[nx][ny] });
            }
        }
        for (int i = 1; i <= n; i++, puts(" "))
            for (int j = 1; j <= m; j++) cout << h[i][j] - a[i][j] << " ";
    
        return 0;
    }
    

    思路2

    (前方高能,非战斗人员请迅速撤离)
    建边用两点中高度最大的点的高度为边权,考虑一个问题,一条路径中最大的边权就是这个坑的最大深度,因为要是有更大的路径都没有选择。本蒟蒻目前还没有完全理解,等悟了之后一定重新缕一边思路,目前只悟了这么点。

    #include<bits/stdc++.h>
    using namespace std;
    const int M=310*310,N=310;
    struct Edge{
    	int fr,to,nxt,val;
    	bool operator < (const Edge&A)const{
    		return val<A.val;
    	}
    }e[M],t[M<<2];
    struct Node{
    	int x,y;
    	Node(){}
    	Node(int a,int b){
    		x=a;y=b;
    	}
    	bool operator < (const Node&A)const{
    		return x<A.x;
    	}
    };
    map<int,Node> col;
    int h[M],idx;
    void Ins(int a,int b,int c){
    	e[++idx].to=b;e[idx].nxt=h[a];
    	h[a]=idx;e[idx].val=c;
    }
    int n,m;
    int Mat[N][N],mp[N][N],ans[N][N],cnt;
    int f[M];
    int find(int x){
    	return x==f[x]?x:(f[x]=find(f[x]));
    }
    void dfs(int u,int fa,int Mx){
    	Node now=col[u];
    	if(Mat[now.x][now.y]<=Mx)ans[now.x][now.y]=Mx-Mat[now.x][now.y];
    	else ans[now.x][now.y]=0;
    	for(int i=h[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==fa)continue;
    		dfs(v,u,max(Mx,e[i].val));
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	col[0]=Node(0,0);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			scanf("%d",&Mat[i][j]);
    			mp[i][j]=++cnt;
    			col[cnt]=Node(i,j);
    		}
    	}
    	for(int i=1;i<=cnt;i++)f[i]=i;
    	cnt=0;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			t[++cnt].fr=mp[i][j];
    			t[cnt].to=mp[i][j+1];
    			t[cnt].val=max(Mat[i][j],Mat[i][j+1]);
    			t[++cnt].fr=mp[i][j];
    			t[cnt].to=mp[i+1][j];
    			t[cnt].val=max(Mat[i][j],Mat[i+1][j]);
    			t[++cnt].fr=mp[i][j];
    			t[cnt].to=mp[i][j-1];
    			t[cnt].val=max(Mat[i][j],Mat[i][j-1]);
    			t[++cnt].fr=mp[i][j];
    			t[cnt].to=mp[i-1][j];
    			t[cnt].val=max(Mat[i][j],Mat[i-1][j]);
    		}
    	}
    	sort(t+1,t+cnt+1);
    	for(int i=1;i<=cnt;i++){
    		int u=find(t[i].fr),v=find(t[i].to);
    		if(u!=v){
    			f[u]=v;
    			Ins(u,v,t[i].val);Ins(v,u,t[i].val);
    		}
    	}
    	dfs(0,-1,-0x7fffffff);
    	for(int i=1;i<=n;i++,puts(""))
    	for(int j=1;j<=m;j++)
    		printf("%d ",ans[i][j]);
    }
    

    找伙伴

    题目描述

    在班级里,每个人都想找学习伙伴。伙伴不能随便找,都是老师固定好的,老师给出要求:伙伴要凭自己实力去找。
    老师给每个人发一张纸,上面有数字。假设你的数字是w,那么如果某位同学手中的数字的所有正约数之和等于w,那么这位同学就是你的小伙伴。

    输入格式

    输入包含n组数据(最多100组)
    对于每组测试数据,输入只有一个数字w。

    输出格式

    对于每组数据输出两行,第一行包含一个整数m,表示有m(如果m = 0,只输出一行0即可)个伙伴。
    第二行包含相应的m个数,表示伙伴的数字。注意:小伙伴的数字必须按照升序排列。

    样例

    (input)

    
    

    (output)

    
    

    思路

    简单的数论

    暴力算法(显然会TLE)

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define rint register int
    using namespace std;
    inline int read(){
    	int x = 0, f = 1; char ch = getchar();
    	for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    	return x * f;
    }
    const int maxn = 1e8 + 50;
    
    int a[maxn], cnt, col[maxn];
    bool flag;
    
    inline void solve(int x,int k){
    	col[x] = 0;
    	int sol = sqrt(x);
    	for(rint i = 1; i <= sol; i++)
    		if(x % i == 0){ col[x] += i;col[x] += x/i;}
    	if(sol * sol == x){ col[x] -= sol;}
    	if(col[x] == k){ flag = true;a[++cnt] = x;}
    	col[x] = col[x];
    	return;
    }
    
    int main(){
    	//freopen("a.in", "r", stdin);
    	int n;
    	while(~scanf("%d", &n)){
    		cnt = 0, flag = 0;
    		for(rint i = 1; i <= n; ++i){
    			if(col[i] != n && col[i]) continue;
    			else solve(i, n);
    		}
    		if(flag == false){
    			printf("0
    ");
    			continue;
    		}
    		printf("%d
    ", cnt);
    		for(rint i = 1; i < cnt; ++i){
    			printf("%d ", a[i]);
    		}
    		printf("%d
    ", a[cnt]);
    	}
    	return 0;
    }
    

    正解算法

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 5e4;
    bool is_not_prime[maxn];
    int prime[maxn];
    int m;
    void primes(int n) {
        is_not_prime[1] = 1;
        for (int i = 2; i <= n; i++) {
            if (!is_not_prime[i])
                prime[++prime[0]] = i;
            for (int j = 1; i * prime[j] <= n; j++) {
                is_not_prime[i * prime[j]] = 1;
                if (i % prime[j] == 0)
                    break;
            }
        }
    }
    bool is_prime(int x) {
        if (x <= maxn)
            return !is_not_prime[x];
        for (int i = 1; prime[i] * prime[i] <= x; i++) {
            if (x % prime[i] == 0)
                return 0;
        }
        return 1;
    }
    int a[maxn], cnt;
    void dfs(int now, int p, int x) {
        if (now == 1) {
            a[++cnt] = x;
            return;
        }
        if (is_prime(now - 1) && now > prime[p])
            a[++cnt] = x * (now - 1);
        for (int i = p; prime[i] * prime[i] <= now; i++) {
            int pi = prime[i];
            int sum = prime[i] + 1;
            for (; sum <= now; pi *= prime[i], sum += pi) {
                if (now % sum == 0)
                    dfs(now / sum, i + 1, x * pi);
            }
        }
    }
    int main() {
        primes(maxn);
        while (scanf("%d", &m) != EOF) {
            cnt = 0;
            dfs(m, 1, 1);
            printf("%d
    ", cnt);
            sort(a + 1, a + cnt + 1);
            for (int i = 1; i <= cnt; i++) {
                printf("%d ", a[i]);
            }
            printf("
    ");
        }
    }
    

    D.string

    题目描述

    给定一个由小写字母组成的字符串 s。有 m 次操作,每次操作给定 3 个参数 l,r,x。如果 x=1,将 s[l]∼s[r] 升序排序;如果 x=0,将 s[l] s[r] 降序排序。你需要求出最终序列。

    输入格式

    第一行两个整数 n,m,表示字符串长度为n,有m次操作。
    第二行一个字符串s。
    接下来m行每行三个整数 l,r,x。

    输出格式

    一行一个字符串表示答案。

    样例

    (input)

    5 2
    cabcd
    1 3 1
    3 5 0
    

    (output)

    abdcc
    

    思路

    根据v数据范围可以看出来单纯的sort肯定解决不了问题,再想一下,英文字母只有26个,那么这么大的序列里面肯定会有很多一样的字符,看数据范围和操作的大概样式,可以联想到线段树,要是sort的话可以把很多个字符一起sort,可以大大提高效率。

    代码实现

    
    
    #include <cstdio>
    #include <cstring>
    #define lson (p * 2)
    #define rson (p * 2 + 1)
    #define mid ((l + r >> 1))
    using namespace std;
    char s[100005];
    int n, m, l, r, x, tree[400005], cnt[30];
    void build(int p, int l, int r) {
    	if (l == r) return tree[p] = s[l] - 'a' + 1, void();
    	build(lson, l, mid), build(rson, mid + 1, r);
    	tree[p] = (tree[lson] == tree[rson]) ? tree[lson] : 0;
    }
    void query(int p, int l, int r, int s, int t) {
    	if (s <= l && r <= t && tree[p]) return cnt[tree[p]] += r - l + 1, void();
    	if (tree[p] && l != r) tree[lson] = tree[p], tree[rson] = tree[p];
    	if (s <= mid) query(lson, l, mid, s, t);
    	if (t > mid) query(rson, mid + 1, r, s, t);
    }
    void update(int p, int l, int r, int s, int t, int v) {
    	if (s <= l && r <= t || tree[p] == v) return tree[p] = v, void();
    	if (tree[p] && l != r) tree[lson] = tree[p], tree[rson] = tree[p];
    	if (s <= mid) update(lson, l, mid, s, t, v);
    	if (t > mid) update(rson, mid + 1, r, s, t, v);
    	tree[p] = (tree[lson] == tree[rson]) ? tree[lson] : 0;
    }
    void print(int p, int l, int r) {
    	if (l == r || tree[p]) for (int i = l; i <= r; i++) putchar(tree[p] + 'a' - 1);
    	else print(lson, l, mid), print(rson, mid + 1, r);
    }
    int main() {
    	scanf("%d%d%s", &n, &m, s + 1);
    	build(1, 1, n);
    	for (int i = 1; i <= m; i++) {
    		scanf("%d%d%d", &l, &r, &x);
    		memset(cnt, 0, sizeof(cnt));
    		query(1, 1, n, l, r);
    		if (x) {
    			for (int i = 1; i <= 26; i++) 
    				if (cnt[i]) update(1, 1, n, l, l + cnt[i] - 1, i), l = l + cnt[i];
    		} else {
    			for (int i = 26; i >= 1; i--) 
    				if (cnt[i]) update(1, 1, n, l, l + cnt[i] - 1, i), l = l + cnt[i];
    		}
    	}
    	print(1, 1, n);
    	return 0;
    }
    
  • 相关阅读:
    NSRunLoop的利用
    快速排序算法
    WebViewJavascriptBridge的暂时理解
    非常喜欢的一期《晓松奇谈》
    字符串正则替换replace第二个参数是函数的问题
    Model模型和Module模块的区别
    jQuery的extend方法的深层拷贝
    正则表达式学习记录
    select2初始化默认值
    增进编程语言学习速度的小技巧
  • 原文地址:https://www.cnblogs.com/hzoi-liujiahui/p/13418364.html
Copyright © 2011-2022 走看看