AtCoder Beginner Contest 264 Solution更好的阅读体验戳此进入题面链接题面 Luogu 链接abcd 不说了[ABC264E] Blackout 2题面SolutionCode[ABC264F] Monochromatic Path题面SolutionCode[ABC264G] String Fair题面SolutionCode[ABC264Ex] Perfect Binary Tree题面SolutionCodeUPD
ZK 国有
这些地点的标号为
这个国家有
现在有
每次询问后输出有供电的城市。
没什么可说的,比较显然,把询问离线然后倒着做,用并查集维护加边即可,经典套路。
xxxxxxxxxx
931
2
3
4
5
6
7
8
9
10using namespace std;
11
12mt19937 rnd(random_device{}());
13int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
14bool rnddd(int x){return rndd(1, 100) <= x;}
15
16typedef unsigned int uint;
17typedef unsigned long long unll;
18typedef long long ll;
19typedef long double ld;
20
21template < typename T = int >
22inline T read(void);
23
24struct Edge{int s, t;}edg[510000];
25int N, M, E;
26int Q;
27ll rans[510000];
28bool del[510000];
29int qs[510000];
30ll ans(0);
31
32class UnionFind{
33private:
34 int fa[510000];
35 int siz[510000];
36 bool light[510000];
37public:
38 UnionFind(void){for(int i = 1; i <= 501000; ++i)fa[i] = i;}
39 void Init(void){
40 for(int i = 1; i <= N; ++i)siz[i] = 1;
41 for(int i = N + 1; i <= N + M; ++i)light[i] = true;
42 }
43 int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
44 void Merge(int s, int t){
45 int rs = Find(s), rt = Find(t);
46 if(rs == rt)return;
47 // printf("Mergeing rs = %d, rt = %d, light is %d %d\n", rs, rt, light[rs] ? 1 : 0, light[rt] ? 1 : 0);
48 if(light[rs] ^ light[rt]){
49 if(!light[rs] && light[rt])swap(rs, rt);
50 ans += siz[rt];
51 siz[rs] += siz[rt];
52 fa[rt] = rs;
53 }else{
54 siz[rs] += siz[rt];
55 fa[rt] = rs;
56 }
57 }
58}uf;
59
60int main(){
61 // freopen("test_18.txt", "r", stdin);
62 // freopen("out.txt", "w", stdout);
63 N = read(), M = read(), E = read();
64 uf.Init();
65 for(int i = 1; i <= E; ++i){
66 int s = read(), t = read();
67 edg[i] = Edge{s, t};
68 }Q = read();
69 for(int i = 1; i <= Q; ++i)del[qs[i] = read()] = true;
70 for(int i = 1; i <= E; ++i)if(!del[i])uf.Merge(edg[i].s, edg[i].t);//, printf("merge : %d %d\n", edg[i].s, edg[i].t);
71 for(int i = Q; i >= 1; --i){
72 rans[i] = ans;
73 uf.Merge(edg[qs[i]].s, edg[qs[i]].t);
74 }for(int i = 1; i <= Q; ++i)printf("%lld\n", rans[i]);
75 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
76 return 0;
77}
78
79template < typename T >
80inline T read(void){
81 T ret(0);
82 int flag(1);
83 char c = getchar();
84 while(c != '-' && !isdigit(c))c = getchar();
85 if(c == '-')flag = -1, c = getchar();
86 while(isdigit(c)){
87 ret *= 10;
88 ret += int(c - '0');
89 c = getchar();
90 }
91 ret *= flag;
92 return ret;
93}
给定一个
首先不难想到每一行或一列最多进行一次反转操作,否则是无用的。发现只能向右或向下,则无后效性,故可以尝试 DP。
设
我们设当前状态为
对于向右走的下一步也同理可得。
初始值即为
最终答案即为
xxxxxxxxxx
711
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22template < typename T = int >
23inline T read(void);
24
25int H, W;
26ll R[2100], C[2100];
27ll dp[2100][2100][2][2];
28bitset < 2100 > G[2100];
29
30int main(){
31 H = read(), W = read();
32 for(int i = 1; i <= H; ++i)R[i] = read();
33 for(int i = 1; i <= W; ++i)C[i] = read();
34 for(int i = 1; i <= H; ++i)
35 for(int j = 1; j <= W; ++j){
36 char c = getchar(); while(!isdigit(c))c = getchar();
37 G[i][j] = c == '1' ? true : false;
38 }
39 memset(dp, 0x3f, sizeof dp);
40 dp[1][1][0][0] = 0, dp[1][1][1][0] = R[1], dp[1][1][0][1] = C[1], dp[1][1][1][1] = R[1] + C[1];
41 for(int i = 1; i <= H; ++i)
42 for(int j = 1; j <= W; ++j)
43 for(int x = 0; x <= 1; ++x)
44 for(int y = 0; y <= 1; ++y){
45 if((G[i][j] ^ x ^ y )== (G[i + 1][j] ^ y))dp[i + 1][j][0][y] = min(dp[i + 1][j][0][y], dp[i][j][x][y]);
46 else dp[i + 1][j][1][y] = min(dp[i + 1][j][1][y], dp[i][j][x][y] + R[i + 1]);
47 if((G[i][j] ^ x ^ y) == (G[i][j + 1] ^ x))dp[i][j + 1][x][0] = min(dp[i][j + 1][x][0], dp[i][j][x][y]);
48 else dp[i][j + 1][x][1] = min(dp[i][j + 1][x][1], dp[i][j][x][y] + C[j + 1]);
49 }
50 ll ans(LONG_LONG_MAX);
51 for(int x = 0; x <= 1; ++x)for(int y = 0; y <= 1; ++y)ans = min(ans, dp[H][W][x][y]);
52 printf("%lld\n", ans);
53 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
54 return 0;
55}
56
57template < typename T >
58inline T read(void){
59 T ret(0);
60 int flag(1);
61 char c = getchar();
62 while(c != '-' && !isdigit(c))c = getchar();
63 if(c == '-')flag = -1, c = getchar();
64 while(isdigit(c)){
65 ret *= 10;
66 ret += int(c - '0');
67 c = getchar();
68 }
69 ret *= flag;
70 return ret;
71}
给定 Infinity
。
一个十分精妙的图论。
关键的信息在
于是考虑如对于从
这样将图建完之后不难发现只需要 SPFA 跑一遍单源最长路,取最大的
于是点数为
当然这里我们用 map
实现,手动实现一个 hash()
之后用 unorderer_map
即可去掉 map
的
xxxxxxxxxx
1041
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27struct Edge{
28 Edge* nxt;
29 int to;
30 ll val;
31 OPNEW;
32}ed[21000];
33ROPNEW(ed);
34Edge* head[1100];
35
36int N;
37int cnt(0);
38unordered_map < string, int > score;
39map < pair < int, int >, int > mp;
40unordered_map < int, pair < int, int > > rmp;
41bitset < 1100 > inq;
42int dep[1100];
43ll dis[1100];
44ll ans(LONG_LONG_MIN);
45
46void SPFA(void){
47 memset(dis, 0xc0, sizeof dis);
48 queue < int > cur;
49 cur.push(1); inq[1] = true; dep[1] = 1, dis[1] = 0;
50 while(!cur.empty()){
51 int p = cur.front(); cur.pop();
52 inq[p] = false;
53 for(auto i = head[p]; i; i = i->nxt){
54 if(dis[p] + i->val > dis[SON]){
55 dis[SON] = dis[p] + i->val;
56 ans = max(ans, dis[SON]);
57 dep[SON] = dep[p] + 1;
58 if(dep[SON] > 26 * 26 + 26 + 1)printf("Infinity\n"), exit(0);
59 if(!inq[SON])cur.push(SON), inq[SON] = true;
60 }
61 }
62 }
63}
64
65int main(){
66 N = read();
67 for(int i = 1; i <= N; ++i){
68 string S; cin >> S;
69 score.insert({S, read()});
70 }mp.insert({{0, 0}, ++cnt}), rmp.insert({cnt, {0, 0}});
71 for(int i = 'a'; i <= 'z'; ++i)mp.insert({{0, i}, ++cnt}), rmp.insert({cnt, {0, i}});
72 for(int i = 'a'; i <= 'z'; ++i)for(int j = 'a'; j <= 'z'; ++j)
73 mp.insert({{i, j}, ++cnt}), rmp.insert({cnt, {i, j}});
74 for(int i = 1; i <= cnt; ++i)for(int j = 'a'; j <= 'z'; ++j){
75 ll tot(0); string S;
76 if(rmp[i].first)S += (char)rmp[i].first;
77 if(rmp[i].second)S += (char)rmp[i].second;
78 S += j; tot += score[S];
79 while(!S.empty()){
80 S.erase(S.begin());
81 tot += score[S];
82 }
83 head[i] = new Edge{head[i], mp[{rmp[i].second, j}], tot};
84 }SPFA();
85 printf("%lld\n", ans);
86 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
87 return 0;
88}
89
90template < typename T >
91inline T read(void){
92 T ret(0);
93 int flag(1);
94 char c = getchar();
95 while(c != '-' && !isdigit(c))c = getchar();
96 if(c == '-')flag = -1, c = getchar();
97 while(isdigit(c)){
98 ret *= 10;
99 ret += int(c - '0');
100 c = getchar();
101 }
102 ret *= flag;
103 return ret;
104}
存在一棵以
首先一个比较重要的性质就是
不难想到 DP,令
朴素的转移十分显然,即在其儿子里找到两个层高为
考虑到我们每次询问都是增加一个节点,如此的转移方式复杂度显然是不正确的,考虑优化。不难想到这个式子可以转化为从和平方里去除平方和,即:
于是这个东西就可以支持
最终的答案即为
所以每次修改都是
xxxxxxxxxx
1101
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27struct Edge{
28 Edge* nxt;
29 int to;
30 OPNEW;
31}ed[610000];
32ROPNEW(ed);
33Edge* head[310000];
34
35ll qpow(ll a, ll b){
36 ll ret(1), mul(a);
37 while(b){
38 if(b & 1)ret = ret * mul % MOD;
39 b >>= 1;
40 mul = mul * mul % MOD;
41 }return ret;
42}
43
44int N;
45int mxdep;
46int inv2;
47int dep[310000];
48int fa[310000];
49ll ans(0);
50ll sum[310000][21], sum2[310000][21];
51
52ll DP(int p, int i){
53 if(i == 1)return 1;
54 return ((sum[p][i] * sum[p][i] % MOD - sum2[p][i]) % MOD + MOD) % MOD * inv2 % MOD;
55}
56void dfs(int p = 1, int fa = 0){
57 dep[p] = dep[fa] + 1;
58 for(auto i = head[p]; i; i = i->nxt)
59 if(SON != fa)dfs(SON, p);
60}
61
62int main(){
63 // freopen("in.txt", "r", stdin);
64 // freopen("out.txt", "w", stdout);
65 inv2 = qpow(2, MOD - 2);
66 N = read();
67 if(N == 1)printf("1\n"), exit(0);
68 mxdep = (int)log2(N);
69 for(int i = 2; i <= N; ++i){
70 fa[i] = read();
71 head[i] = new Edge{head[i], fa[i]};
72 head[fa[i]] = new Edge{head[fa[i]], i};
73 }dfs();
74 for(int cp = 1; cp <= N; ++cp){
75 if(dep[cp] <= mxdep){
76 sum[cp][1] = sum2[cp][1] = 1;
77 ll cnt(1), cur(cp), lst(-1), lstDP(0);
78 while(cur != 1){
79 lst = cur;
80 cur = fa[cur], ++cnt;
81 ll tmp = DP(cur, cnt);
82 (((sum[cur][cnt] -= lstDP) %= MOD) += MOD) %= MOD;
83 (((sum2[cur][cnt] -= lstDP * lstDP % MOD) %= MOD) += MOD) %= MOD;
84 lstDP = tmp;
85 (sum[cur][cnt] += DP(lst, cnt - 1)) %= MOD;
86 (sum2[cur][cnt] += DP(lst, cnt - 1) * DP(lst, cnt - 1) % MOD) %= MOD;
87 }
88 }ans = 0;
89 for(int i = 1; i <= mxdep; ++i)(ans += DP(1, i)) %= MOD;
90 printf("%lld\n", ans);
91 }
92 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
93 return 0;
94}
95
96template < typename T >
97inline T read(void){
98 T ret(0);
99 int flag(1);
100 char c = getchar();
101 while(c != '-' && !isdigit(c))c = getchar();
102 if(c == '-')flag = -1, c = getchar();
103 while(isdigit(c)){
104 ret *= 10;
105 ret += int(c - '0');
106 c = getchar();
107 }
108 ret *= flag;
109 return ret;
110}
update-2023_01_03 初稿