码迷,mamicode.com
首页 > 其他好文 > 详细

[题解向] 正睿Round409

时间:2019-10-27 21:09:02      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:自己   dag   out   lock   背包   ioi   最长路   集合   class   

\(\rm Link\)

然而泥萌没有权限是看不了题目的233.

\(\rm T1\)

大概就是个map,脑残出题人认为(x,x)不属于有序二元组,我可qtmd。于是只拿了\(\rm 60pts\)

int main(){
    ios_base :: sync_with_stdio(false) ;
    cin.tie(0), cout.tie(0) ; cin >> N >> P ; int i ; 
    for (i = 1 ; i <= N ; ++ i) cin >> base[i], base[i] %= P ; 
    for (i = 1 ; i <= N ; ++ i) if (base[i]) M[expow(4 * base[i] % P, P - 2)] ++ ;
    for (i = 1 ; i <= N ; ++ i){
        int now = 1ll * base[i] * base[i] % P ; 
        ans += M[now] ; if (4ll * now % P * base[i] % P == 1) -- ans ;  
    }
    cout << ans << endl ; return 0 ;    
} 

\(\rm T2\)

先说自己的做法。

发现其实就是在凑一个式子:

\[\frac{\sum a_ik_i}{M\sum k_i}=\frac{N}{M}\]

然后稍微变个形:

\[\sum a_ik_i=N\cdot \sum k_i\]

于是发现只要暴力背包就完了,最后判断一下\(f_{k,N}\)是不是\(=k\)就做完了。

int main(){
    cin >> K >> N >> M ; int i, j ; dp[0] = 0 ; 
    for (i = 1 ; i <= K ; ++ i) cin >> base[i] ; 
    for (i = 1 ; i <= N * N ; ++ i) dp[i] = Inf ; 
    for (i = 1 ; i <= K ; ++ i)
        for (j = base[i] ; j <= N * N ; ++ j)
            dp[j] = min(dp[j - base[i]] + 1, dp[j]) ; 
    for (i = 1 ; i <= N ; ++ i) if (dp[i * N] == i) return cout << i, 0 ;
    return -1 ;  
}

于是获得了\(\rm 70pts\)……但是用头想一想,发现并不对。因为可能\(f_{k,N}\)这个状态的\(\rm cost\)可能是5,但是背包转移的时候只能记录最优状态,于是假设会记录4,那这个状态就挂掉了……对每个状态都开了个std :: set发现T掉了……

正解是BFS。

考虑把式子转化一下,就变成了

\[\sum k_i(a_i-N)=0\]

大概就是考虑用已经凑出的和当做状态,那么实际上是在找一个环。于是每次转移的时候枚举放哪个新物品(新物品权值为\(a_i-N\))就完了。

#define MAX 50000
    cin >> K >> N >> M ; q.push(MAX) ; vis[MAX] = 1 ; int i ;
    for (i = 1 ; i <= K ; ++ i) cin >> base[i], base[i] -= N ; 
    while (!q.empty()){
        int now = q.front() ; q.pop() ; 
        if (now > M + MAX) continue ;
        for (i = 1 ; i <= K ; ++ i){
            if (now + base[i] == MAX){
                cout << ans[now] + 1 << endl ; 
                return 0 ; 
            }
            else if (!vis[now + base[i]]) 
                vis[now + base[i]] = 1, ans[now + base[i]] = ans[now] + 1, q.push(now + base[i]) ;
        }
    }
    cout << -1 << endl ; 

正 解 暴 力, 菜 鸡 退 役。

\(\rm T3\)

sb题,每个连通块都产生\(\rm 1\)的贡献当且仅当没有任何一个连通块是树。

于是就变成了找树的问题……忘记前驱思考了nm老久,趁早退役算了= =

void dfs(int now, int pre){
    vis[now] = 1 ;
    for (int k = head[now] ; k ; k = E[k].next){
        if (to(k) == pre) continue ; 
        if (to(k) == now) continue ; 
        if (!vis[to(k)]) dfs(to(k), now) ; else ++ o ; 
    } 
}
int main(){
    cin >> N ; int i ; p = 1 ; 
    for (i = 1 ; i <= N ; ++ i) fa[i] = i ; 
    for (i = 1 ; i <= N ; ++ i) cin >> base[i], add(i, base[i]) ; 
    for (i = 1 ; i <= N ; ++ i) if (fa[i] == i) ++ ans ;// cout << ans << endl ; 
    for (i = 1 ; i <= N ; ++ i) if (!vis[i]) o = 0, dfs(i, 0), p = min(o, p) ; 
    if (p) cout << ans << endl ; else cout << ans - 1 << endl ; 

\(\rm T4\)

BZOJ4160.

不可做题233

题面:

给定一张无向图,求给这张图定向成\(\rm DAG\)之后最长路最短是多少。\(n\leq 16\)

\(\rm{Sol~1}\)

考虑直接\(dp\)\(f_{s,u,v}\)表示考虑了点集\(s\),最长路端点是\(u,v\)的最小值。每次转移的时候枚举\(u,v,w\),从\((u,v),(v,w)\)两个状态转移到\((u,w)\)。复杂度\(O(3^nn^3)\)

1h没调出来

\(\rm Sol~2\)

考虑证明一个二级定理:

定理 \(1.1\)

  • 一张无向图定向成的\(\rm DAG\),当其最长路最短时,其最长路为\(\rm X-1\),其中\(\rm X\)表示不连通集覆盖数。也就是对于一张有向图图\(\rm \{V,E\}\),定义一种划分\(P\),使得划分出的每个集合中所有点不连通。

考虑一种证明:

  • 首先一定有\(\rm X-1\geq maxL\)。因为其上的每个点是连通的。
  • 其次我们考虑,如果每次删除全部出度为\(0\)点,放到一个集合里,那么一定合法,并且可以满足\(\rm X=maxL+1\)

但其实这东西也可以直接用\(\rm dilworth\)定理的对偶定理证出来:

定理 \(2.1\)\(\rm dilworth\)定理)

\(\rm (X,≤)\)是一个有限偏序集,并令\(m\)是反链的最大的大小。则\(\rm X\)可以被划分成\(m\)个但不能再少的链。

对偶一下:

定理 \(2.2\):
\(\rm (X,≤)\)是一个有限偏序集,并令\(r\)是其最大链的大小。则\(X\)可以被划分成\(r\)个但不能再少的反链。

然后”反链“连接的是”不可比的点“,也就是本题中”不连通的点“。

于是我们就可以快乐地状压了。

int *g, *f ;
int main(){
    cin >> N >> M ; 
    memset(f, 63, sizeof(f)) ; 
    int u, v, i, j ; T = (1 << N) - 1 ;
    for (i = 1 ; i <= M ; ++ i)
        cin >> u >> v, -- u, -- v, E[u] |= (1 << v), E[v] |= (1 << u) ;
    f[0] = 0, g[0] = 1 ; 
    for (i = 0 ; i <= N ; ++ i) Sz[1 << i] = i ; 
    for (i = 1 ; i <= T ; ++ i){
        j = (i & (-i)) ;
        if (!g[i ^ j]) continue ; 
        if (E[Sz[j]] & (i ^ j)) continue ; 
        g[i] = 1 ; 
    }
    for (i = 1 ; i <= T ; ++ i)
        for (j = i ; j ; j = (j - 1) & i)
            if (g[j]) f[i] = min(f[i], f[i ^ j] + 1) ; 
    cout << f[T] - 1 << endl ; return 0 ;
}

[题解向] 正睿Round409

标签:自己   dag   out   lock   背包   ioi   最长路   集合   class   

原文地址:https://www.cnblogs.com/pks-t/p/11748764.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!