2024.11.14
今日总结:
上午复习了线段树,树状数组,主席树,并查集,下午主要是做南外的题
1:https://www.luogu.com.cn/problem/P1442
这道题是一道用线段树来维护Dp的题目,难点在于要把平台离散化,转移方程很好写
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define INF 1 << 30
struct Node
{
int shu, cover;
} segtree[MAXN << 2];
struct DD
{
int ld, rd, hd;
} kuai[MAXN];
bool cmp(DD a, DD b)
{
return a.hd < b.hd;
}
int pai[MAXN];
int paitail = 1;
int duiy[MAXN];
int f[MAXN][2];
int hhnext[MAXN][2];
int start_next;
int n, maxt, startx, starty;
inline void pushup(int cur)
{
segtree[cur].shu = max(segtree[cur << 1].shu, segtree[cur << 1 | 1].shu);
return;
}
inline void pushdown(int cur)
{
if (segtree[cur].cover == 0)
return;
segtree[cur << 1].cover = segtree[cur << 1 | 1].cover = segtree[cur].cover;
segtree[cur << 1].shu = segtree[cur << 1 | 1].shu = segtree[cur].cover;
segtree[cur].cover = 0;
return;
}
inline int query(int cur, int l, int r, int L, int R)
{
if (L <= l && r <= R)
{
return segtree[cur].shu;
}
pushdown(cur);
int ans = 0;
int mid = (l + r) >> 1;
if (L <= mid)
ans = max(ans, query(cur << 1, l, mid, L, R));
if (R > mid)
ans = max(ans, query(cur << 1 | 1, mid + 1, r, L, R));
return ans;
}
inline void add(int cur, int l, int r, int L, int R, int t)
{
if (L <= l && r <= R)
{
segtree[cur].shu = segtree[cur].cover = t;
return;
}
pushdown(cur);
int mid = (l + r) >> 1;
if (L <= mid)
add(cur << 1, l, mid, L, R, t);
if (R > mid)
add(cur << 1 | 1, mid + 1, r, L, R, t);
pushup(cur);
return;
}
int main()
{
scanf("%d%d", &n, &maxt);
scanf("%d%d", &startx, &starty);
for (register int i = 1; i <= n; i++)
{
scanf("%d%d%d", &kuai[i].hd, &kuai[i].ld, &kuai[i].rd);
pai[paitail++] = kuai[i].ld;
pai[paitail++] = kuai[i].rd;
}
sort(pai + 1, pai + paitail);
for (register int i = 1; i <= n; i++)
{
int searchh = lower_bound(pai + 1, pai + paitail, kuai[i].ld) - pai;
duiy[searchh] = kuai[i].ld;
kuai[i].ld = searchh;
searchh = lower_bound(pai + 1, pai + paitail, kuai[i].rd) - pai;
duiy[searchh] = kuai[i].rd;
kuai[i].rd = searchh;
}
sort(kuai + 1, kuai + n + 1, cmp);
for (register int i = 1; i <= n; i++)
{
hhnext[i][0] = query(1, 1, 200000, kuai[i].ld, kuai[i].ld);
if (kuai[i].hd - kuai[hhnext[i][0]].hd > maxt)
hhnext[i][0] = -1;
hhnext[i][1] = query(1, 1, 200000, kuai[i].rd, kuai[i].rd);
if (kuai[i].hd - kuai[hhnext[i][1]].hd > maxt)
hhnext[i][1] = -1;
add(1, 1, 200000, kuai[i].ld, kuai[i].rd, i);
}
int h = lower_bound(pai + 1, pai + paitail, startx) - pai;
start_next = query(1, 1, 200000, h, h);
if (starty - kuai[start_next].hd > maxt)
start_next = -1;
for (register int i = 0; i <= n; i++)
{
f[i][0] = f[i][1] = INF;
}
f[start_next][0] = abs(duiy[kuai[start_next].ld] - startx);
f[start_next][1] = abs(duiy[kuai[start_next].rd] - startx);
for (register int i = start_next; i >= 1; i--)
{
if (hhnext[i][0] != -1)
{
f[hhnext[i][0]][0] = min(f[hhnext[i][0]][0], f[i][0] + (hhnext[i][0] == 0 ? 0 : abs(duiy[kuai[hhnext[i][0]].ld] - duiy[kuai[i].ld])));
f[hhnext[i][0]][1] = min(f[hhnext[i][0]][1], f[i][0] + (hhnext[i][0] == 0 ? 0 : abs(duiy[kuai[hhnext[i][0]].rd] - duiy[kuai[i].ld])));
}
if (hhnext[i][1] != -1)
{
f[hhnext[i][1]][0] = min(f[hhnext[i][1]][0], f[i][1] + (hhnext[i][1] == 0 ? 0 : abs(duiy[kuai[hhnext[i][1]].ld] - duiy[kuai[i].rd])));
f[hhnext[i][1]][1] = min(f[hhnext[i][1]][1], f[i][1] + (hhnext[i][1] == 0 ? 0 : abs(duiy[kuai[hhnext[i][1]].rd] - duiy[kuai[i].rd])));
}
}
int ans = min(f[0][0], f[0][1]);
ans += starty;
printf("%d\n", ans);
return 0;
}
2:http://www.nfls.com.cn:20035/contest/2090/problem/7
这道题主要考察的是单调队列优化Dp,需要用数组模拟单调队列,写完Dp转移方程后看可不可以提出公因数/式这样的可以用单调队列优化
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 10;
const int M = 510;
const int INF = 1e15;
int n,m,ep,head,tail;
int dp[M][N],dx[M],q[N];
struct Node
{
int x,c,w;
}a[N];
bool cmp(Node &a,Node &b)
{
return a.x < b.x;
}
signed main()
{
scanf("%lld%lld%lld",&ep,&m,&n);
for(int i = 1;i <= n;i ++)
scanf("%lld%lld%lld",&a[i].x,&a[i].c,&a[i].w);
n ++;
a[n].x = m;
sort(a + 1,a + n + 1,cmp);
for(int i = 2;i <= n;i ++)
dx[i] = a[i].x - a[i - 1].x;
for(int i = 0;i <= n;i ++)
for(int j = 0;j <= ep;j ++)
dp[i][j] = INF;
dp[0][0] = 0;
for(int i = 1;i <= n;i ++)
{
head = tail = 0;
for(int j = 0;j <= ep;j ++)
{
while(head < tail && j - q[head] > a[i - 1].c)
head ++;//若超出库存弹出买入元素
if(dp[i - 1][j] != INF)
{
while(head < tail)
{
int k = q[tail - 1];
if(dp[i - 1][k] - k * a[i - 1].w < dp[i - 1][j] - j * a[i - 1].w) break;
tail --;
}
q[tail ++] = j;
}
if(head < tail)
{
int k = q[head];
dp[i][j] = dp[i - 1][k] + (j - k) * a[i - 1].w + j * j * dx[i];
}
}
}
printf("%lld\n",dp[n][ep]);
return 0;
}
3:[http://www.nfls.com.cn:20035/contest/2083/problem/1](Why Did the Cow Cross the Road I G)
这道题考察的是最短路和Dp相结合,也可以用分层图来解决,
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,T,ans;
int mp[N][N],vis[N][N][4];
int dx[5] = {0,0,-1,1},dy[5] = {-1,1,0,0};//左右上下
struct Node
{
int x,y,step,c;
}st;
void bfs()
{
queue<Node> q;
memset(vis,0x3f,sizeof(vis));
st.x = 1,st.y = 1,st.step = 0,st.c = 0;
vis[1][1][0] = 0;
q.push(st);
while(!q.empty())
{
Node t = q.front();
q.pop();
for(int i = 0;i < 4;i ++)//向四个方向走
{
int xx = t.x + dx[i],yy = t.y + dy[i];
if(xx >= 1 && xx <= n && yy >= 1 && yy <= n)
{
Node d;
d.step = (t.step + 1) % 3;//预知下一步操作
if(d.step == 0) d.c = t.c + T + mp[xx][yy];
else d.c = t.c + T;
if(d.c < vis[xx][yy][d.step])
{
d.x = xx,d.y = yy;
vis[xx][yy][d.step] = d.c;
q.push(d);
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&T);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
scanf("%d",&mp[i][j]);
bfs();
ans = min(vis[n][n][0],min(vis[n][n][1],vis[n][n][2]));
printf("%d\n",ans);
return 0;
}
4:[http://www.nfls.com.cn:20035/contest/2083/problem/2](Minimum Transport Cost)
这道题主要是难在如何输出最短路的路径,我们可以考虑记录每个最短路的路径中第一个经过的点即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
int n,m;
int dis[N][N],cost[N],dist[N][N],pos[N];
int main()
{
scanf("%d%d",&n,&m);
memset(dis,0x3f,sizeof(dis));
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= n;j ++)
{
scanf("%d",&dis[i][j]);
dist[i][j] = dis[i][j];
}
dis[i][i] = 0;
dist[i][i] = 0;
}
for(int i = 1;i <= n;i ++)
scanf("%d",&cost[i]);
for(int k = 1;k <= n;k ++)
{
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= n;j ++)
{
dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j] + cost[k]);
}
}
}
while(m --)
{
memset(pos,0,sizeof(pos));
int x,y;
scanf("%d%d",&x,&y);
cout << "From " << x << " to " << y << " :\n";
int total = dist[x][y];
int now = x;
cout << "Path: " << x;
pos[x] = 1;
while(now != y)
{
for(int i = 1;i <= n;i ++)
{
if(i == now || pos[i]) continue;
if(i == y && dis[now][i] == dist[now][i]) cout << "-->" << i,now = i;
if(dis[now][i] + cost[i] + dist[i][y] == dist[now][y]) cout << "-->" << i,now = i,pos[i] = 1;
}
}
cout << '\n';
cout << "Total cost : " << total << '\n' << "\n";
}
return 0;
}
5:[http://www.nfls.com.cn:20035/contest/2083/problem/3](MOR-Tales of seafaring)
这道题主要考察的是在一张图上每条边可以反复的走,这种情况可以只去考虑与终点相连的路径的长度与总最短路长度的奇偶性,只要满足相同即可,注意考虑数组开的范围否则会MLE
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
const int M = 5e7 + 10;
int n,m,k;
int headd,tail;
short q[M];
int dis[N][N];
int head[N],to[N],nxt[N],idx;
void Add(int a,int b)
{
to[++ idx] = b;
nxt[idx] = head[a];
head[a] = idx;
to[++ idx] = a;
nxt[idx] = head[b];
head[b] = idx;
}
void dijkstra(int i)//求出所有点对的最短距离
{
q[1] = i;
headd = tail = 1;
while(headd <= tail)
{
int x = q[headd];
headd ++;
for(int j = head[x];j;j = nxt[j])
{
int v = to[j];
if(!dis[i][v])
{
dis[i][v] = dis[i][x] + 1;
q[++ tail] = v;
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i <= m;i ++)
{
int x,y;
scanf("%d%d",&x,&y);
Add(x,y + n);
Add(y,x + n);
}
for(int i = 1;i <= n;i ++)
dijkstra(i);
while(k --)
{
int s,t,d;
scanf("%d%d%d",&s,&t,&d);
if(d % 2 == 1) if(dis[s][t + n] <= d && dis[s][t + n]) {puts("TAK"); continue;}
if(d % 2 == 0) if(dis[s][t] <= d && dis[s][t]) {puts("TAK"); continue;}
puts("NIE");
}
return 0;
}