HK2016训练赛总结
HK2016训练赛
C
by 🐱49min
- 想想\(k>1\)且\(k<n-2\)时,答案是啥?
- \(k=0,k=n-1\)时,非常简单。
- \(k=1\)or\(k=n-1\)时,对a``b分别维护前缀,后缀最大值最小值。然后枚举哪个元素比较孤傲即可。
B
by🐻66min
- 求一些点到直线距离取min
D
by🐻130min
- dp[i][j][k]: 使得前- i栋楼,上升序列长度为- j,第- i栋楼高度为- k的最小耗费。
- 然后显然,我们应该对高度离散化。因为这里要求严格递增,所以我们先h[i]+=i,然后再离散化,这样就变成非严格单增问题了。
K
by🐶187min
- 把每个集合当成一个节点。如果集合S是T1,T2,T3...的并。那么我们把T1,T2,T3...当成集合S的儿子。
- 关于怎么建树,我们考虑元素x,如果x被集合set1,set2,set3...包含。那我们对set1,set2,...按照从小到大的顺序排序,然后,第i个集合的爸爸是第i+1个集合。
J
by🐻220min
- 把所有串插入AC自动机。
- 如果某个状态包含了某个禁止的串,那么这个状态我们删掉。对于剩下的状态,我们建图。
- 如果建出来的图成环,那么输出-1
- 否则DAG上求最长路即可。
A
by🐱290min
- 我们可以在200步操作内,将u,v交换颜色,或者将v的颜色变成u的颜色。
- 我们先通过交换,使得每种颜色至少有一个节点,走到正确位置。
- 然后对于每种颜色,我们拿着那个位置正确的点,修改掉其他位置不正确的点的颜色即可。
补题
H
- 从大到小,依次加边。加边的同时,进行并查集合并。
- 枚举最大割边的权值上界\(w\),要最小化slim值,就是要让两个集合的size尽可能的接近。
- 权值\(\leq w\)的边,会把\(n\)个点,分成很多个联通块,大小为\(k\)的联通块,相当于体积为\(k\)的物品,为了让两个集合size接近,我们选择一些物品,使得体积和最接近\(n/2\)
两个做法
做法1:
- 体积为\(k\)的物品,可以看成多项式\(1+x^k\)
- 添加一个物品,可以看成乘上\((1+x^k)\)
- 弃置一个物品,可以看成乘上\((1+x^k)^{-1}\)
- 我们先添加\(n\)个,大小为1的物品。
- 合并两个大小为\(k_1,k_2\)的物品,就相当于弃二摸一啦。
- 然后每次乘法的复杂度是\(O(n)\)的。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 30000+10;
int n,m,par[N],sz[N];
struct Edge{
    int u,v,w;
    bool operator < (const Edge & o) const {
        return w > o.w;
    }
} edge[N];
int find(int x){
    return par[x]==x?x:par[x]=find(par[x]);
}
int dp[N];
void ItemUPD(int val,int on) {
    //printf("val=%d, on=%d\n", val, on);
    if(on==+1) {
        for(int i=n;i>=val;i--) dp[i]=(dp[i]+dp[i-val])%MOD;
    }
    if(on==-1) {
        for(int i=val;i<=n;i++) dp[i]=(dp[i]-dp[i-val]+MOD)%MOD;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) par[i]=i,sz[i]=1;
    dp[0]=1;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        edge[i].u++, edge[i].v++;
    }
    sort(edge+1,edge+1+m);
    double ans = 1e12;
    for(int i=1;i<=n;i++) ItemUPD(1,+1);
    for(int i=1;i<=m;i++) {        
        int u=edge[i].u;int v=edge[i].v;
        if(find(u)==find(v)) continue;
        for(int j=n/2;j>=1;j--){
            if(dp[j]) {
                ans=min(ans, 1.0*edge[i].w/j);break;
            }
        }
        ItemUPD(sz[find(u)], -1);
        ItemUPD(sz[find(v)], -1);
        sz[find(v)]+=sz[find(u)];par[find(u)]=find(v);
        ItemUPD(sz[find(v)], +1);
    }
    printf("%.12f\n", ans);
}
做法2:时间魔术!CDQ分治
- 我们来施展时间魔术!每秒钟加入一条边。
- 然后每个物品都会有个出现的时间,有个消失的时间。那么每个物品存在的时间都是一个区间
- 我们尝试着拿一个bitset维护,哪些体积是能够凑出来的。
- 该怎么算时间\([L,R]\)内,的答案呢?
- 考虑体积为\(V\),对应的区间\([x,y]\)的物品。
- 如果\([x,y]\)包含\([L,R]\)。那么此物品,对于\([L,R]\)的每个时间点,都既可以拿,也可以不拿。我们直接更新bitset啦,\(B=B|(B<<V)\)
- 否则的话,令\(mid=(L+R)/2\)
- 如果\(y\geq mid+1\),那么这个物品会影响\([mid+1,R]\),我们把这样的物品放入【垃圾桶A】
- 如果\(x \leq mid\),那么这个物品会影响\([L,mid]\),我们把这样的物品放入【垃圾桶B】
 
- 递归地,拿垃圾桶A去,更新\([L,mid]\),拿垃圾桶B去更新\([mid+1,R]\)
 
- 如果\([x,y]\)包含\([L,R]\)。那么此物品,对于\([L,R]\)的每个时间点,都既可以拿,也可以不拿。我们直接更新
- 注意,与dfs的弹栈类似,处理完一个区间后,应该将bitset,还原到刚开始处理这个区间的状态。
从题目风格来看,这套题和国内画风不太一致。
国内的硬核数学题,计算几何题,数据结构题在区域赛上出现得相当之多。一旦遇到这种题,团队配合很容易脱节。前几日的CCPC吉林,最后两个小时,🐱卡在一个数据结构上,🐶卡在一个模拟上,🐱和🐶基本上被分割到了战场的两侧,无法形成任何呼应。🐻后期的存在感也相当淡薄。这种局势显然是极度不利的。
但从这套题来看,“重意识,轻操作”是一个很大的特点。也是说,不需要多深的数学底蕴,亦不需多高超的操作技巧,合理的想法所及之处,便是AC。从我方特点来看.......操作真的很烂很烂了。
- 🐶的码力虽有,但操作技巧完全跟不上当今比赛的节奏,启发式合并,虚树,这些“有竞赛技巧的操作”掌握得极度生疏,日常靠乱搞暴力过题。
- 🐱的码力就是个黑洞&硬核数学太弱,
- 🐻也是个乱搞流选手,的数据结构掌握得十分搞笑&huge计算几何写下来比赛早结束了。
也是说,一旦碰上硬核的东西,我军势孤,必败。
但是香港赛区的题,似乎并不看重这些东西。
- 🐱CF打得多,前期输出稳定,反应不至于太慢,建模意识不差。
- 🐻的乱搞常有奇效,计算几何意识很佳【CCPC的E起码第一反应是解方程】,30行以内的贪心直觉相当准。
- 🐶的乱搞常有奇卜。所以苟🐶是沙比。
然而,重意识,而对操作要求低的东西,我们更容易形成配合,打出自己的节奏。
从比赛过程来看,节奏掌控得不错。
- 开场🐻几乎把所有题读了一遍。然后🐱发现了C签到,用了20min的样子过了。
- 之后🐻上B,🐱理论了J,K没想好树怎么建。
- 之后🐱上J,然后AC自动机里面,判定一个状态是否包含某个禁止串呢个地方写黄了,WA。
- 🐻想出了D题的DP,自己过掉了。
- 之后🐱想好了K怎么建树,然后告诉了苟狗,苟🐶开始写。
- 🐱和🐻一边fix``J,一边讨论A.但没很大进展。
- 🐶过了K后,🐻重写了J,1Y
- 之后🐱和🐻次饭去了,苟狗这个沙比啥也不会。
- 吃完饭回来,🐱经过一些思路上的调整,过了A
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号