sc2017新高二&高一模拟赛8 总结

前言:又来水一波总结。。(为什么我“万径人踪灭”调不出来啊啊啊)


题目描述

商店里出售n种不同品种的花。为了装饰桌面,你打算买m支花回家。你觉得放两支一样的花很难看,因此每种品种的话最多买1支。求总共有几种不同的买花的方案?答案可能很大,输出答案mod p的值。

对于30%的数据,n,m≤10
对于50%的数据,n,m≤1000
另有20%的数据,n≤50,000,m≤100
对于100%的数据,1≤m≤n≤50,000,p≤1,000,000,000


输入格式

一行3个整数n,m,p,意义如题所述。


输出格式

一个整数,表示买花的方案数。


输入样例

4 2 5


输出样例

1

用数字1,2,3,4来表示花的种类的话,4种花里买各不相同的2支的方案有(1,2)、(1,3)、(1,4)、(2,3)、(2,4)、(3,4),共6种方案,模5后余数是1。


题解(组合数+分解质因数)

签到题,答案就是 CmnMod p ,一开始想直接求逆元,后来才意识到 p 不一定是质数。那我们化乘为加,直接分解质因数就好了。
时间复杂度O(nn)


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define MAXN 50010

using namespace std;

typedef long long LL;
int n, m, p;
int prime[MAXN];
LL ans;


void Work(int num, int v){
    for(int i = 1; i <= num; i++){
      int temp = i;
      for(int j = 2; j * j <= temp; j++)
        while(temp % j == 0){
          temp /= j;
          prime[j] += v;
        }
      if(temp != 1)  prime[temp] += v;
    }
}

int main(){
    scanf("%d%d%d", &n, &m, &p);
    Work(n, 1);
    Work(m, -1);
    Work(n-m, -1);

    ans = 1ll;
    for(int i = 1; i <= n; i++)
      while(prime[i]){
        prime[i] --;
        ans = ans * i % p;
      }

    printf("%lld\n", ans);
    return 0;
}

手套

题目描述

你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。

30%的数据N≤9;
60%的数据N≤1000;
100%的数据N≤200,000。


输入格式

输入第一行一个N,表示手套对数。
第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。


输出格式

一行,包含一个数,表示最少交换次数。


输入样例

2
0 1 0 1


输出样例

1

将中间两个手套交换过来,颜色序列变成0 0 1 1。


题解(树状数组求逆序对)

一开始yy了一会儿。
如果暴力,就是模拟后面的往前面拖的过程。按第一次出现的位置重新标号,逆序对数就是答案。

用BIT或者归并排序就行了。
话说好像用线段树的数组都开小了。。(不能AK的遗憾)

时间复杂度 O(nlogn)


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define MAXN 200010

using namespace std;

typedef long long LL;
int n, color[MAXN<<2], fir[MAXN], cnt;
LL BIT[MAXN], Ans;
bool vis[MAXN];

int lowbit(int x){
    return x & (-x);
}

void Add(int x){
    for(int i = x; i <= n; i += lowbit(i))  BIT[i] ++;
}

LL Sum(int x){
    LL res = 0;
    for(int i = x; i > 0; i -= lowbit(i))  res += BIT[i];
    return res;
}

int main(){

    scanf("%d", &n);

    for(int i = 1; i <= (n<<1); i++){
      scanf("%d", &color[i]);
      if(!vis[color[i]]){
        vis[color[i]] = true;
        fir[color[i]] = ++cnt;
      }
    }

    for(int i = 1; i <= (n<<1); i++){
      Ans += Sum(n) - Sum(fir[color[i]]);
      Add(fir[color[i]]);
    }
    printf("%lld\n", Ans);

    return 0;
}

星座

题目描述

星空中有n颗星星,有n-1对星星间被人为地连上了线,每条连线有各自的长度。所有星星被连成了一个整体。现在,你要在星系中找到一个最大的十字形星座。即,你要找到两条星星构成的路径,使得它们恰好经过一个公共点(这颗公共点不能是某条路径的端点),且两条路径的长度和最大。
左图红线表示了一个合法的十字形星座,而右图的星座并不合法。

这里写图片描述

20%的数据n<=1000
50%的数据n<=10,000
100%的数据n<=100,000,0<=z<=1000


输入格式

第一行一个数n,表示星星的数量。
接下来n行,每行3个数x,y,z,表示第x颗星星和第y颗星星间有一条连线,它的长度是z。


输出格式

一行,包含一个整数,表示最大的路径长度和。若答案不存在,输出-1。


输入样例

10
3 8 6
9 3 5
1 9 2
4 8 6
2 3 3
10 4 8
5 9 5
7 2 3
6 9 1


输出样例

33

选择9号点为星座中心的那个点。两条路径为6-9-3-8-10和5-9-1,公共点为9


题解(树形DP)

这题算是这场比赛中最值得总结的一题了。

看懂题目的就会发现,这就是HDU Computer那题的进阶嘛。一个裸的树形DP,两遍DFS,一切就结束了。

先讲讲我一开始的想法,一看要维护5个,就觉得很烦。于是一开始不想写树形DP,想了想求树的直径之类的。真是无聊啊。还好最后没时间了,我停止yy,干脆就写了个树形DP,这才发现并没有一开始想得那么难写。

首先规定根为1,我们记 f[u][j] 为第 u 个点往下的第j+1长的不共边路径。总共只用维护四个,dfs一次,用一个点的儿子的 f[v][0] +边去更新就行了,其他的没有任何卵用(因为一个儿子最多贡献一条路径)。注意转移的顺序,要是 f[u][0] 被更新了,那么 f[u][3]=f[u][2] f[u][2]=f[u][1] ,等等。

然后处理完向下的,按照经典的求离一个点的最远点的做法,我们该求连上去的路径了。记其为 g[u] 。这里写法和Computer一样的,用父亲 u 更新儿子v,看看儿子在不在最长路径上,在就用 max(f[u][0],g[u]) +边去更新,否则用 max(f[u][1],g[u]) +边,注意这次先更新再递归,与上一次相反。

然后就求出了离一个点前4长的路径了,以此点为交点,算出最长两条共点路径就不成问题了。判-1的话,直接看看是否存在点有3个儿子且不是根或是根但有4个儿子。这里我的判定有些不严谨(有bug),不过既然都过了就懒得改了。

说得很清楚了,图就不画了。(图在草稿纸上就行了)

时间复杂度: O()


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN 100010

using namespace std;

int n, f[MAXN][5], g[MAXN], ans = -1;
int cur = -1, head_p[MAXN];
struct Tadj{int next, obj, len;} Edg[MAXN<<1];

void Insert(int a, int b, int c){
    cur ++;
    Edg[cur].next = head_p[a];
    Edg[cur].obj = b;
    Edg[cur].len = c;
    head_p[a] = cur;
}

void dfs1(int root, int fa){
    for(int i = head_p[root]; ~ i; i = Edg[i].next){
      int v = Edg[i].obj, l = Edg[i].len;
      if(v == fa)  continue;
      dfs1(v, root);
      if(f[v][0] + l > f[root][0]){ 
        f[root][3] = f[root][2];
        f[root][2] = f[root][1];
        f[root][1] = f[root][0];
        f[root][0] = f[v][0] + l;
      }
      else if(f[v][0] + l > f[root][1]){
        f[root][3] = f[root][2];
        f[root][2] = f[root][1];
        f[root][1] = f[v][0] + l;
      }
      else if(f[v][0] + l > f[root][2]){
        f[root][3] = f[root][2];
        f[root][2] = f[v][0] + l;
      }
      else if(f[v][0] + l > f[root][3])
        f[root][3] = f[v][0] + l;
    }
}

void dfs2(int root, int fa){
    for(int i = head_p[root]; ~ i; i = Edg[i].next){
      int v = Edg[i].obj, l = Edg[i].len;
      if(v == fa)  continue;
      if(f[v][0] + l == f[root][0])
        g[v] = l + max(f[root][1], g[root]);
      else
        g[v] = l + max(f[root][0], g[root]);
      dfs2(v, root);
    }
}

int main(){

    scanf("%d", &n);

    for(int i = 1; i <= n; i++)  head_p[i] = -1;

    int a, b, c;
    for(int i = 1; i < n; i++){
      scanf("%d%d%d", &a, &b, &c);
      Insert(a, b, c);
      Insert(b, a, c);
    }

    dfs1(1, 0);
    dfs2(1, 0);

    for(int i = 1; i <= n; i++){
      int cnt = 0, temp = f[i][0] + f[i][1] + f[i][2] + max(f[i][3], g[i]);
      if(f[i][0])  cnt ++;
      if(f[i][1])  cnt ++;
      if(f[i][2])  cnt ++;
      if(f[i][3])  cnt ++;
      if(g[i])  cnt ++;
      if(cnt >= 4)  ans = max(ans, temp);
    }

    printf("%d\n", ans);

    return 0;
} 

总结

大佬们走后,接连几次的题目都偏易了。这次比赛有些6,赛前根本不知道,打完球从体育馆出来才发现比赛已经开始2min了,然后18:45开始的比赛,八点多才开始码题。还好最后AK了,话说去打球的几个人考得都挺好的,3人AK,一人只有T2卡掉10分。。

Keep.


这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值