A、B、C略
D - Choose Me
设$S$为选择的进行演讲的城镇的集合。那么两个人的选票差为
$$
\sum_{i\in S}(2\times a[i]+b[i])-\sum_{i=1}^na[i]
$$
要使T的选票多于A的选票,即找到一个元素最小的集合$S$,使得该式大于0。
直接按照$2\times a[i]+b[i]$从大到小排序即可。
E - Through Path
每次操作均是对一颗子树或者一颗子树的补集加上一个$x_i$,最后离线询问每一个点的点权。
直接dfs求出dfn,维护子树关系,然后按照dfn差分数组即可。
F - Close Group
这一题比较有意思,当场没有想出来,主要也是没有写过这个类型的题目。
大意是给定一个$n$点$m$边的简单无向图,求出通过删除一定的边后,使得图中的每一个连通分量都是完全图的最小连通分量个数是多少。
解法:首先枚举每一个状态$i$,判断该状态是否构成完全图,如果是,则标记$good[i]=1$。设$dp[i]$为选择了子图$i$后,子图中满足要求的最小连通分量个数。初始化$dp[0]=0$,其它为正无穷。枚举每一个状态,若$good[i]=1$,则$dp[i]=1$;否则,枚举状态的每一个子集j,有方程$dp[i]=min(dp[i],dp[j]+dp[i\hat{}j])$。将状态i用i的子集j和j的补i^j进行转移的原因是:假设存在i是由多个子集共同组合而成是最优的,则这多个子集组合而成的i的子集显然不是最优的,这与假设矛盾。
那么如何枚举每一个子集呢?
对于二进制状态$S$,可以用此方法来进行不重不漏地枚举。
for (int sub = S; sub; sub = (sub - 1) & S) { | 
    // sub 为 S 的子集 | 
} | 
假设当前子集$sub=(d_n d_{n-1} \cdots d_k 10\cdots 0)_2$
那么$sub-1=(d_n d_{n-1} \cdots d_k 01\cdots 1)_2$
(sub-1)&S得到的自然就是二进制表示小于sub的最大的子集 。
枚举的时间复杂度$O(3^n)$:
$$
\sum_{i=0}^n\binom{n}{i}\cdot2^i=(1+2)^n
$$
#参考代码
 | 
 | 
using namespace std; | 
typedef long long ll; | 
const int maxn = 1 << 18; | 
int n, m; | 
bool adj[20][20], good[maxn]; | 
int dp[maxn]; | 
inline bool judge(int mask) { | 
    vector<int> temp; | 
    for(int i = 0; i < n; ++i) | 
        if(mask >> i & 1) temp.push_back(i); | 
    for(int i = 0; i < temp.size(); ++i) | 
        for(int j = i + 1; j < temp.size(); ++j) { | 
            int u = temp[i], v = temp[j]; | 
            if(!adj[u][v]) | 
                return false; | 
        } | 
    return true; | 
} | 
int main() {	 | 
    ios::sync_with_stdio(false); | 
    cin.tie(nullptr), cout.tie(nullptr); | 
    cin >> n >> m; | 
    for(int i = 1; i <= m; ++i) { | 
        int u, v; | 
        cin >> u >> v; | 
        u--, v--; | 
        adj[u][v] = adj[v][u] = 1; | 
    } | 
    for(int i = 0; i < (1 << n); ++i) | 
        good[i] = judge(i); | 
    memset(dp, 0x3f3f3f3f, sizeof(dp)); | 
    dp[0] = 0; | 
    for(int i = 1; i < (1 << n); ++i) { | 
        if(good[i]) | 
            dp[i] = 1; | 
        else { | 
            for (int j = i; j; j = (j - 1) & i)  | 
                dp[i] = min(dp[i], dp[j] + dp[i ^ j]); | 
        } | 
    } | 
    cout << dp[(1 << n) - 1]; | 
    return 0; | 
} |