前言:又来水一波总结。。(为什么我“万径人踪灭”调不出来啊啊啊)
花
题目描述
商店里出售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
不一定是质数。那我们化乘为加,直接分解质因数就好了。
时间复杂度
代码
#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
个点往下的第
然后处理完向下的,按照经典的求离一个点的最远点的做法,我们该求连上去的路径了。记其为
g[u]
。这里写法和Computer一样的,用父亲
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.