[HDU6166]Senior Pan 题解
题目地址:HDUOJ …
May all the beauty be blessed.
题目地址:Codeforces:Problem – 295B – Codeforces、洛谷:【CF295B】Greg and Graph – 洛谷
Greg has a weighed directed graph, consisting of n vertices. In this graph any pair of distinct vertices has an edge between them in both directions. Greg loves playing with the graph and now he has invented a new game:
Help Greg, print the value of the required sum before each step.
给一个完全图的邻接矩阵,按顺序删点,求每一次删点前剩下的点两两最短路长度的和。
输入格式:
The first line contains integer n (1 ≤ n ≤ 500) — the number of vertices in the graph.
Next n lines contain n integers each — the graph adjacency matrix: the j-th number in the i-th line aij (1 ≤ aij ≤ 105, aii = 0) represents the weight of the edge that goes from vertex i to vertex j.
The next line contains n distinct integers: x1, x2, …, xn (1 ≤ xi ≤ n) — the vertices that Greg deletes.
输出格式:
Print n integers — the i-th number equals the required sum before the i-th step.
Please, do not use the %lld specifier to read or write 64-bit integers in C++. It is preferred to use the cin, cout streams of the %I64d specifier.
输入样例#1:
1 0 1
输出样例#1:
0
输入样例#2:
2 0 5 4 0 1 2
输出样例#2:
9 0
输入样例#3:
4 0 3 1 1 6 0 400 1 2 4 0 1 1 1 1 0 4 1 2 3
输出样例#3:
17 23 404 0
n的范围很小,用O(n^2)的枚举求和是可行的,但是删点这个就不怎么好办了。
正难则反,如果把删点改成加点是否可以呢?
多源最短路让我们想到了Floyd,其实Floyd的实质是一个DP,由于压过维,所以有点看不出,实际上这个式子可以写成这样
dp[k][i][j]= \min (dp[k-1][i][j], dp[k-1][i][k] + dp[k-1][k][j])
如果在这个意义下,我们发现第一维维k的时候指的是只有1~k的图中的最短路。我们考虑把给出的排列反着来跑,计算出x[k]~x[n]的图的最短路,再把这些点的最短路每个算一遍加起来,加进答案序列。
// Code by KSkun, 2018/3
#include <cstdio>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 505;
int n, dis[MAXN][MAXN], x[MAXN];
LL ans[MAXN];
int main() {
n = readint();
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
dis[i][j] = readint();
}
}
for(int i = 1; i <= n; i++) {
x[i] = readint();
}
for(int k = n; k >= 1; k--) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
dis[i][j] = std::min(dis[i][j], dis[i][x[k]] + dis[x[k]][j]);
}
}
for(int i = n; i >= k; i--) {
for(int j = n; j >= k; j--) {
ans[k] += dis[x[i]][x[j]];
}
}
}
for(int i = 1; i <= n; i++) {
printf("%I64d ", ans[i]);
}
return 0;
}
题目地址:Codeforces:Problem – 666B – Codeforces、洛谷:【CF666B】World Tour – 洛谷
In some country there are exactly n cities and m bidirectional roads connecting the cities. Cities are numbered with integers from 1 to n. If cities a and b are connected by a road, then in an hour you can go along this road either from city a to city b, or from city b to city a. The road network is such that from any city you can get to any other one by moving along the roads.
You want to destroy the largest possible number of roads in the country so that the remaining roads would allow you to get from city s1 to city t1 in at most l1 hours and get from city s2 to city t2 in at most l2 hours.
Determine what maximum number of roads you need to destroy in order to meet the condition of your plan. If it is impossible to reach the desired result, print -1.
求最多能删去的边数,且删去后满足dis[s1][t1]<=l1,dis[s2][t2]<=l2。
输入格式:
The first line contains two integers n, m (1 ≤ n ≤ 3000, n - 1 \leq m \leq \min\{3000, \frac{n(n-1)}{2}\}) — the number of cities and roads in the country, respectively.
Next m lines contain the descriptions of the roads as pairs of integers ai, bi (1 ≤ ai, bi ≤ n, ai ≠ bi). It is guaranteed that the roads that are given in the description can transport you from any city to any other one. It is guaranteed that each pair of cities has at most one road between them.
The last two lines contains three integers each, s1, t1, l1 and s2, t2, l2, respectively (1 ≤ si, ti ≤ n, 0 ≤ li ≤ n).
输出格式:
Print a single number — the answer to the problem. If the it is impossible to meet the conditions, print -1.
输入样例#1:
5 4 1 2 2 3 3 4 4 5 1 3 2 3 5 2
输出样例#1:
0
输入样例#2:
5 4 1 2 2 3 3 4 4 5 1 3 2 2 4 2
输出样例#2:
1
输入样例#3:
5 4 1 2 2 3 3 4 4 5 1 3 2 3 5 1
输出样例#3:
-1
首先BFS预处理出所有点之间的最短路。如果dis[s1][t1]和dis[s2][t2]没删边就已经不满足了那删边是没有用的,这种情况就是无解的情况。
接着考虑在原本的最短路上有什么方法可以使最短路覆盖的边数减少。我们可以让两条路径中间有一段重合,兴许能减少边数。为什么是一段?因为如果有多段重合,把多段重合之间没重合的改成重合的显然更优。这样我们枚举重合的一段的端点,并且计算总长,更新答案。需要注意的是,s1/s2→u→v→t1/t2与s1→u→v→t1/s2→v→u→t2这两种情况都要考虑,因为是无向图可以双向通行嘛。
复杂度O(n^2)。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 3005;
int n, m, ut, vt, s1, t1, l1, s2, t2, l2, dis[MAXN][MAXN], ans;
bool vis[MAXN];
std::vector<int> gra[MAXN];
std::queue<int> que;
inline void bfs(int st) {
int *dist = dis[st];
memset(vis, 0, sizeof(vis));
que.push(st);
vis[st] = true;
dist[st] = 0;
while(!que.empty()) {
int u = que.front();
que.pop();
for(int v : gra[u]) {
if(vis[v]) continue;
vis[v] = true;
dist[v] = dist[u] + 1;
que.push(v);
}
}
}
inline void updateans(int u, int v) {
int c1 = dis[s1][u] + dis[u][v] + dis[v][t1], c2 = dis[s2][u] + dis[u][v] + dis[v][t2], c = c1 + c2 - dis[u][v];
if(c1 <= l1 && c2 <= l2) ans = std::min(ans, c);
c2 = dis[s2][v] + dis[u][v] + dis[u][t2], c = c1 + c2 - dis[u][v];
if(c1 <= l1 && c2 <= l2) ans = std::min(ans, c);
}
int main() {
memset(dis, 0x3f, sizeof(dis));
n = readint(), m = readint();
for(int i = 1; i <= m; i++) {
ut = readint(), vt = readint();
gra[ut].push_back(vt);
gra[vt].push_back(ut);
}
s1 = readint(), t1 = readint(), l1 = readint();
s2 = readint(), t2 = readint(), l2 = readint();
for(int i = 1; i <= n; i++) {
bfs(i);
}
if(dis[s1][t1] > l1 || dis[s2][t2] > l2) {
printf("-1");
return 0;
}
ans = dis[s1][t1] + dis[s2][t2];
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
updateans(i, j);
}
}
printf("%d", m - ans);
return 0;
}
题目地址:51Nod:子集价值 问题 – 51Nod
lyk最近在研究位运算。它发现除了xor,or,and外还有很多运算。它新定义了一种运算符“#”。具体地,可以由4个参数来表示。ai,j表示i#j。其中i,j与a的值均∈[0,1]。当然问题可以扩展为>1的情况,具体地,可以将两个数分解为p位,然后对于每一位执行上述的位运算,再将这个二进制串转化为十进制就可以了。例如当 a0,0=a1,1=0,a0,1=a1,0=1时,3#4在p=3时等于7,2#3在p=4时等于1(实际上就是异或运算)。
现在lyk想知道的是,已知一个数列b。它任意选取一个序列c,满足 c1<c2<…<ck,其中1≤c1且ck≤n ,这个序列的价值为 bc1 # bc2 #…# bck 的平方。这里我们假设k是正整数,因此满足条件的c的序列一定是 2n−1 。lyk想知道所有满足条件的序列的价值总和是多少。例如样例中,7个子集的价值分别为1,1,4,4,9,9,0。总和为28。
由于答案可能很大,只需对1,000,000,007取模即可。
输入格式:
第一行两个整数n(1<=n<=50000),p(1<=p<=30)。
第二行4个数表示a0,0,a0,1,a1,0,a1,1。(这4个数都∈{0,1})
第三行n个数bi(0<=bi<2^p)。
输出格式:
一行表示答案。
输入样例#1:
3 30 0 1 1 0 1 2 3
输出样例#1:
28
又是一道喜闻乐见的数位DP。
首先这个平方给我们设置了一个障碍,我们如果不求平方只求和,那么我们可以设计状态dp[i][0/1]表示当前处理到第i个数,现在正在处理的这一位经过运算后为0或1的方案总数。我们从大到小枚举每一位,每一位对答案的贡献就是1<<数*dp[n][1]。
但是现在有一个平方,那我们考虑平方的意义。如果把每一位单独拆出来算,那么一个十进制数可以表示成一堆2的幂加起来的结果,又由于我们有下面的式子
(A_1+A_2+A_3+A_4+\cdots+A_n)^2 = A_1A_2 + \cdots + A_1A_n + A_2A_1 + \cdots + A_2A_n + \cdots + A_n^2
我们发现,平方后的结果的每一项只跟原来的两项有关系。换句话说,原来的数中两个位就能对平方后的答案产生一个位的贡献。那我们考虑把上面求和的DP状态扩展为dp[i][0/1][0/1]表示当前处理到第i个数,选定的两位分别为多少的方案总数。我们枚举选定哪两位来组合出答案,每枚举一次就DP一次,由于两位必须得都是1乘起来才非0,最后枚举的两位产生的贡献就是dp[n][1][1]*1<<(i+j),其中i和j是枚举的两位。
这样做的时间复杂度是O(p^2n)的。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 50005, MO = 1e9 + 7;
int n, p, op[2][2];
LL dp[MAXN][2][2], a[MAXN];
int main() {
n = readint();
p = readint();
op[0][0] = readint();
op[0][1] = readint();
op[1][0] = readint();
op[1][1] = readint();
for(int i = 1; i <= n; i++) {
a[i] = readint();
}
LL ans = 0;
for(int i = 0; i < p; i++) {
for(int j = 0; j < p; j++) {
memset(dp, 0, sizeof(dp));
for(int k = 1; k <= n; k++) {
int p1 = (a[k] >> i) & 1, p2 = (a[k] >> j) & 1;
dp[k][p1][p2] = (dp[k][p1][p2] + 1) % MO;
dp[k][op[0][p1]][op[0][p2]] = (dp[k][op[0][p1]][op[0][p2]] + dp[k - 1][0][0]) % MO;
dp[k][0][0] = (dp[k][0][0] + dp[k - 1][0][0]) % MO;
dp[k][op[0][p1]][op[1][p2]] = (dp[k][op[0][p1]][op[1][p2]] + dp[k - 1][0][1]) % MO;
dp[k][0][1] = (dp[k][0][1] + dp[k - 1][0][1]) % MO;
dp[k][op[1][p1]][op[0][p2]] = (dp[k][op[1][p1]][op[0][p2]] + dp[k - 1][1][0]) % MO;
dp[k][1][0] = (dp[k][1][0] + dp[k - 1][1][0]) % MO;
dp[k][op[1][p1]][op[1][p2]] = (dp[k][op[1][p1]][op[1][p2]] + dp[k - 1][1][1]) % MO;
dp[k][1][1] = (dp[k][1][1] + dp[k - 1][1][1]) % MO;
}
ans = (ans + (1ll << (i + j)) % MO * dp[n][1][1] % MO) % MO;
}
}
printf("%lld", ans);
return 0;
}