2023冲刺国赛模拟 19.1
T1 矩阵
正解做法是二维分块,然而直接上树状数组跑的飞快,不过我写的根号重构,加 Fastio 后可过。
code
#include <cstdio>
#include <algorithm>
#include <ctime>
#include <iostream>
using namespace std;
char buf[1 << 21], *p1, *p2;
inline char gc ()
{
if ( __builtin_expect(p1 == p2, false) )
{
p1 = buf;
p2 = buf + fread(buf, 1, 1 << 21, stdin);
if ( p2 != (buf + ( 1 << 21 )) )
*p2++ = EOF;
}
return *p1++;
}
inline int Read ()
{
int x = 0; char c = gc();
while ( c < '0' || c > '9' )
c = gc();
while ( c >= '0' && c <= '9' )
{
x = ( x << 3 ) + ( x << 1 ) + c - '0';
c = gc();
}
return x;
}
char s[60]; int top;
inline void Write ( long long x )
{
top = 0;
do
{
s[++top] = x % 10 + '0';
x /= 10;
} while ( x );
while ( top )
putchar(s[top--]);
putchar('\n');
return;
}
const int max1 = 1000, lim = 100;
int n, m, q;
struct Point
{
int x, y, w;
}p[lim + 5];
int len;
long long sum[max1 + 5][max1 + 5][2];
int main ()
{
freopen("matrix.in", "r", stdin);
freopen("matrix.out", "w", stdout);
int opt, x, y, z, a, b; long long ans;
n = Read(), m = Read(), q = Read();
while ( q -- )
{
opt = Read(), x = Read(), y = Read();
if ( opt == 1 )
{
z = Read();
sum[x][y][0] += z;
p[++len].x = x, p[len].y = y, p[len].w = z;
if ( len == lim )
{
len = 0;
for ( int i = 1; i <= n; i ++ )
for ( int j = 1; j <= m; j ++ )
sum[i][j][1] = sum[i - 1][j][1] + sum[i][j - 1][1] - sum[i - 1][j - 1][1] + sum[i][j][0];
}
}
else
{
a = Read(), b = Read();
ans = sum[a][b][1] - sum[x - 1][b][1] - sum[a][y - 1][1] + sum[x - 1][y - 1][1];
for ( int i = 1; i <= len; i ++ )
if ( p[i].x >= x && p[i].x <= a && p[i].y >= y && p[i].y <= b )
ans += p[i].w;
Write(ans);
}
}
cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
return 0;
}
T2 种草
首先考虑
建立费用流模型,从
对于区间
仔细思考发现这可以限制每个点不会被超过
由于数据范围很大,因此考虑 Dijikstra 费用流,由于图初始为 DAG ,因此求解每个点的势能可以直接 DP ,复杂度可以做到
考虑解决原问题,由于保证
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int max1 = 1e5;
const long long inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
struct Graph
{
struct Node
{
int next, v, flow;
long long cost;
}edge[max1 * 5 + 5];
int head[max1 + 5], total;
long long h[max1 + 5];
bool vis[max1 + 5];
long long dis[max1 + 5];
int flow[max1 + 5], pre_edge[max1 + 5], pre_point[max1 + 5];
long long maxflow, maxcost;
struct Point
{
int x; long long d;
Point () {}
Point ( int __x, long long __d )
{ x = __x, d = __d; }
bool operator < ( const Point &A ) const
{ return d < A.d; }
};
priority_queue <Point> que;
void Clear ()
{
memset(head, 0, sizeof(int) * ( n + 2 ));
total = 1;
return;
}
void Add ( int u, int v, int f, long long c )
{
edge[++total].v = v;
edge[total].flow = f;
edge[total].cost = c;
edge[total].next = head[u];
head[u] = total;
return;
}
void Add_Edge ( int u, int v, int f, long long c )
{
return Add(u, v, f, c), Add(v, u, 0, -c);
}
void Build ()
{
for ( int i = 0; i <= n + 1; i ++ )
h[i] = -inf;
h[0] = 0;
for ( int now = 0; now <= n; now ++ )
{
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v, f = edge[i].flow;
long long c = edge[i].cost;
if ( f )
h[v] = max(h[v], h[now] + c);
}
}
return;
}
bool Dijikstra ()
{
for ( int i = 0; i <= n + 1; i ++ )
dis[i] = -inf, vis[i] = false;
dis[0] = 0, flow[0] = 0x3f3f3f3f, pre_edge[0] = 0, pre_point[0] = 0;
while ( !que.empty() )
que.pop();
que.push(Point(0, 0));
while ( !que.empty() )
{
int x = que.top().x;
que.pop();
if ( vis[x] )
continue;
vis[x] = true;
for ( int i = head[x]; i; i = edge[i].next )
{
int v = edge[i].v, f = edge[i].flow;
long long c = edge[i].cost + h[x] - h[v];
if ( f && dis[v] < dis[x] + c )
{
dis[v] = dis[x] + c;
flow[v] = min(flow[x], f);
pre_edge[v] = i;
pre_point[v] = x;
que.push(Point(v, dis[v]));
}
}
}
return vis[n + 1];
}
void Update ()
{
for ( int i = 0; i <= n + 1; i ++ )
h[i] += dis[i];
maxflow += flow[n + 1];
maxcost += h[n + 1] * flow[n + 1];
int now = n + 1;
while ( now )
{
edge[pre_edge[now]].flow -= flow[n + 1];
edge[pre_edge[now] ^ 1].flow += flow[n + 1];
now = pre_point[now];
}
return;
}
void EK ()
{
Build(); maxflow = maxcost = 0;
while ( Dijikstra() )
Update();
return;
}
}Graph;
int main ()
{
freopen("weed.in", "r", stdin);
freopen("weed.out", "w", stdout);
scanf("%d%d", &n, &m); Graph.Clear();
int now = 0;
for ( int i = 1, x; i <= n; i ++ )
{
scanf("%d", &x);
while ( now != x )
++now, Graph.Add_Edge(0, i, 1, 0);
Graph.Add_Edge(i, i + 1, x, 0);
}
for ( int i = 1, L, R, w; i <= m; i ++ )
{
scanf("%d%d%d", &L, &R, &w);
Graph.Add_Edge(L, R + 1, 1, w);
}
Graph.EK();
printf("%lld\n", Graph.maxcost);
return 0;
}
T3 基环树
考虑树的情况,一种很显然的 dp 思路是设
考虑
直接做的复杂度为
考虑基环树,由于一次选择的路径最长为
code
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <ctime>
#include <random>
#include <cstring>
using namespace std;
const int max1 = 1e5, B = 300;
const long long inf = 0x3f3f3f3f3f3f3f3f;
mt19937 rnd((unsigned long long)new int);
int n;
struct Node
{
int next, v, w;
}edge[max1 * 2 + 5];
int head[max1 + 5], total, del_edge, point;
int father[max1 + 5], circle[max1 + 5], len;
bool vis[max1 + 5], is_circle[max1 + 5];
struct Point
{
int v, w;
}son[max1 + 5];
int cnt;
long long f[max1 + 5][3], g[max1 * 2 + 5], tmp[max1 * 2 + 5], ans;
void Add ( int u, int v, int w )
{
edge[++total].v = v;
edge[total].w = w;
edge[total].next = head[u];
head[u] = total;
return;
}
bool Find_Circle ( int now, int fa )
{
vis[now] = true; father[now] = fa;
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v;
if ( v == fa )
continue;
if ( !vis[v] )
{
if ( Find_Circle(v, now) )
return true;
}
else
{
len = 0;
int u = now;
while ( u != v )
{
circle[++len] = u;
is_circle[u] = true;
u = father[u];
}
circle[++len] = u;
is_circle[u] = true;
return true;
}
}
return false;
}
void Dfs ( int now, int fa )
{
if ( !is_circle[now] && vis[now] )
return;
vis[now] = true;
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v, w = edge[i].w;
if ( i == del_edge || ( i ^ 1 ) == del_edge || v == fa )
continue;
Dfs(v, now);
}
cnt = 0;
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v, w = edge[i].w;
if ( i == del_edge || ( i ^ 1 ) == del_edge || v == fa )
continue;
son[++cnt].v = v;
son[cnt].w = w;
}
f[now][0] = f[now][1] = f[now][2] = -inf;
if ( cnt <= B )
{
int sum = 0;
g[n] = 0; g[n - 1] = g[n + 1] = -inf;
for ( int i = 1; i <= cnt; i ++ )
{
int v = son[i].v, w = son[i].w;
for ( int k = -sum; k <= sum; k ++ )
tmp[n + k] = g[n + k];
for ( int k = -sum - 1; k <= sum + 1; k ++ )
g[n + k] = -inf;
for ( int k = -sum; k <= sum; k ++ )
{
g[n + k] = max(g[n + k], tmp[n + k] + max(f[v][0], f[v][2] + w));
g[n + k + 1] = max(g[n + k + 1], tmp[n + k] + f[v][0] + w);
g[n + k - 1] = max(g[n + k - 1], tmp[n + k] + f[v][1] + w);
}
++sum;
}
if ( now == point )
{
int w = edge[del_edge].w;
for ( int k = sum; k >= -sum; k -- )
g[n + k + 1] = max(g[n + k + 1], g[n + k] + w);
}
f[now][0] = max(f[now][0], g[n]);
f[now][1] = max(f[now][1], g[n + 1]);
f[now][2] = max(f[now][2], g[n - 1]);
}
else
{
shuffle(son + 1, son + 1 + cnt, rnd);
int sum = 0;
g[n] = 0; g[n - 1] = g[n + 1] = -inf;
for ( int i = 1; i <= cnt; i ++ )
{
int v = son[i].v, w = son[i].w;
for ( int k = -sum; k <= sum; k ++ )
tmp[n + k] = g[n + k];
for ( int k = -sum - 1; k <= sum + 1; k ++ )
g[n + k] = -inf;
for ( int k = -sum; k <= sum; k ++ )
{
g[n + k] = max(g[n + k], tmp[n + k] + max(f[v][0], f[v][2] + w));
g[n + k + 1] = max(g[n + k + 1], tmp[n + k] + f[v][0] + w);
g[n + k - 1] = max(g[n + k - 1], tmp[n + k] + f[v][1] + w);
}
++sum;
sum = min(sum, B);
}
if ( now == point )
{
int w = edge[del_edge].w;
for ( int k = sum; k >= -sum; k -- )
g[n + k + 1] = max(g[n + k + 1], g[n + k] + w);
}
f[now][0] = max(f[now][0], g[n]);
f[now][1] = max(f[now][1], g[n + 1]);
f[now][2] = max(f[now][2], g[n - 1]);
}
return;
}
int main ()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d", &n); total = 1;
for ( int i = 1, u, v, w; i <= n; i ++ )
{
scanf("%d%d%d", &u, &v, &w);
Add(u, v, w), Add(v, u, w);
}
Find_Circle(1, 0); ans = -inf;
memset(vis, 0, sizeof(vis));
for ( int i = 1; i <= 3; i ++ )
{
int pre = i - 1, suf = i + 1;
pre = pre == 0 ? len : pre;
suf = suf == len + 1 ? 1 : suf;
int now = circle[i]; pre = circle[pre], suf = circle[suf];
for ( int k = head[now]; k; k = edge[k].next )
{
int v = edge[k].v;
if ( v == pre || v == suf )
{
del_edge = k;
point = v;
Dfs(now, 0);
ans = max(ans, f[now][0]);
if ( ( i == 1 && v == pre ) || ( i == 3 && v == suf ) )
{
point = now;
Dfs(now, 0);
ans = max(ans, f[now][0]);
}
}
}
}
printf("%lld\n", ans);
return 0;
}
感觉自己题解写的越来越水了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】