2013多校联合训练第三场
2013-07-31 20:54
513 查看
倒着慢慢总结一下吧……
A
一个非常明显的最小割模型,但是需要让一个点向一个矩阵里面的所有点连边,直接连是一定会爆空间/时间的。
所以需要把这个矩阵倍增划分,比较像做RMQ的ST表或者二维线段树。
比如一个2x2的矩阵就要划分为这个样子:
这样划分的话可以把一个点向一个矩阵里面的所有点连边转化为只向至多4个点连边,就可以解决这个题目了。
点数是O(n^2log^2n+Q)级别..有点多,不过dinic还是跑的挺快的
B
后缀自动机学习理解中……
C
题解的一句话提示性很明显,6和12是没有区别的,17,19,23和1也是没有区别的,没有区别的数字之间可以化为一个等价类,这样一共就只有14种等价类,状态最多只有14x1728000种,就可以状压DP了,30s绰绰有余。
J
这道题也是非常有意思的,也比较有启发性。方法题解说的很明白,只是之前不太理解题解说的“拿树状数组维护”,就照着自己的想法用线段树来维护,结果是TLE……
常数优化无果后,就看了看标程理解一下这个树状数组。
这题用数据结构需要支持:
1、单点修改
2、区间查询最大值
单点修改有个特点,一个位置的值只会增加不会减少,所以区间的最大值不会减少。
区间查询也有个特点,就是查询是一个区间[i,x]的最大值,但查询的时候[x+1,n]都是没有值的(0),就可以等价于查询一个[i,n],一个后缀的最大值。
所以就可以套用树状数组的形式来实现这些操作了,只需把后缀倒过来改为前缀即可,效率比线段树快的多得多,是不是我的线段树常数太搓了……
K
题解的solve1非常炫酷……但是不知道为什么我写的会T,后来换了几个更新的顺序(姿势)就AC了,果然这个对代码的效率也有比较大的影响。
A
一个非常明显的最小割模型,但是需要让一个点向一个矩阵里面的所有点连边,直接连是一定会爆空间/时间的。
所以需要把这个矩阵倍增划分,比较像做RMQ的ST表或者二维线段树。
比如一个2x2的矩阵就要划分为这个样子:
这样划分的话可以把一个点向一个矩阵里面的所有点连边转化为只向至多4个点连边,就可以解决这个题目了。
点数是O(n^2log^2n+Q)级别..有点多,不过dinic还是跑的挺快的
B
后缀自动机学习理解中……
C
题解的一句话提示性很明显,6和12是没有区别的,17,19,23和1也是没有区别的,没有区别的数字之间可以化为一个等价类,这样一共就只有14种等价类,状态最多只有14x1728000种,就可以状压DP了,30s绰绰有余。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cstdlib> #include <cmath> #include <cctype> #include <queue> #include <stack> #include <string> #include <set> #include <map> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; #define N 20 typedef long long LL; using namespace std; int n , m , M; struct edge { int x , next; }e[N * N]; int f[1728000][14]; int dir[] = {1 , 2 , 3 , 5 , 6 , 7 , 10 , 11 , 13 , 14 , 15 , 21 , 22 , 26 , 1 << 30}; int bel[] = {0 , 1 , 2 , 3 , 2 , 5 , 6 , 7 , 2 , 3 , 10 , 11 , 6 , 13 , 14 , 15 , 2 , 1 , 6 , 1 , 10 , 21 , 22 , 1 , 6 , 5 , 26 , 3 , 14}; int cnt , ha , power , pre , mcnt; void init() { int i , j; scanf("%d%d",&n,&m); memset(cnt , 0 , sizeof(cnt)); memset(ha , -1 , sizeof(ha)); M = 0; for (i = 0 ; dir[i] <= n ; ++ i) ha[dir[i]] = i , ++ M; for (i = 1 ; i <= n ; ++ i) ++ cnt[ha[bel[i]]]; power[0] = 1; for (i = 0 ; i < M ; ++ i) power[i + 1] = power[i] * (cnt[i] + 1); //cout << power[M] << ":"; memset(pre , -1 ,sizeof(pre)); mcnt = 0; for (i = 0 ; i < M ; ++ i) for (j = i ; j < M ; ++ j) if (__gcd(dir[i] , dir[j]) == 1) { e[mcnt] = (edge){j , pre[i]} , pre[i] = mcnt ++; if (i != j) e[mcnt] = (edge){i , pre[j]} , pre[j] = mcnt ++; } } void work() { int i , j , x , y , k; memset(f , 0 , sizeof(f)); f[0][0] = 1 % m; for (i = 0 ; i < power[M] ; ++ i) for (x = 0 ; x < M ; ++ x) if (f[i][x]) { for (j = pre[x] ; ~j ; j = e[j].next) { y = e[j].x; k = i - i / power[y + 1] * power[y + 1] , k /= power[y]; if (k >= cnt[y]) continue; f[i + power[y]][y] += f[i][x] * (cnt[y] - k); f[i + power[y]][y] %= m; } } int ans = 0; for (i = 0 ; i < M ; ++ i) { ans += f[power[M] - 1][i]; if (ans >= m) ans -= m; } cout << ans << endl; } int main() { int _; cin >> _ ; while (_ --) init() , work(); return 0; }
J
这道题也是非常有意思的,也比较有启发性。方法题解说的很明白,只是之前不太理解题解说的“拿树状数组维护”,就照着自己的想法用线段树来维护,结果是TLE……
常数优化无果后,就看了看标程理解一下这个树状数组。
这题用数据结构需要支持:
1、单点修改
2、区间查询最大值
单点修改有个特点,一个位置的值只会增加不会减少,所以区间的最大值不会减少。
区间查询也有个特点,就是查询是一个区间[i,x]的最大值,但查询的时候[x+1,n]都是没有值的(0),就可以等价于查询一个[i,n],一个后缀的最大值。
所以就可以套用树状数组的形式来实现这些操作了,只需把后缀倒过来改为前缀即可,效率比线段树快的多得多,是不是我的线段树常数太搓了……
K
题解的solve1非常炫酷……但是不知道为什么我写的会T,后来换了几个更新的顺序(姿势)就AC了,果然这个对代码的效率也有比较大的影响。
相关文章推荐
- 2013暑期多校联合训练\第三场\Problem H
- 2013暑期多校联合训练\第三场\Problem G
- 2013多校联合训练第五场
- hdu 4617 2013多校联合训练第二场weapon简单的计算几何
- hdu 4361 2013多校联合训练第3场最后一题
- hdu 4619 warm up 2 并查集或搜索都可以做出来的题 2013多校联合训练第二场
- 2013多校联合训练第四场
- hdu 4607 park visit 2013多校联合训练第一场
- 2013暑期多校联合训练\第五场 Problem F Magic Pen 6
- 2013暑期多校联合训练\第四场\Problem H Hehe
- 2013 多校联合训练一
- hdu 4602 partition 2013多校联合训练第一场
- 2015多校联合训练第三场Painter(hdu5319)
- 2013多校联合4【Problem A: ZZ买衣服】
- (汲取经验)SCU2013多校联合赛
- HDOJ 5792 (2016多校联合训练 Training Contest 5) World is Exploding
- [HDU5828] Rikka with Sequence [2016 Multi-University Training Contest 8(2016多校联合训练8) 1008]
- 2016多校联合训练4 F - Substring 后缀数组
- 2013 多校联合 C Partition (hdu 4602)
- poj 4604 Deque-----2013多校联合赛第一场--1005