9.25李赛 挂成狗了 & 被打爆了
挂成狗了。
A LOJ #6720. 「CodePlus #7」最小路径串
对于每一个点的出边按照到达点的编号大小排序。
dfs 一下。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb emplace_back
typedef long long ll;
typedef std::vector<int> veci;
const ll mod = 998244353;
const int N = 1000010;
int n, m, ent, head[N];
ll dis[N];
bool vis[N];
char ch[N*12];
veci eg[N];
int to_int(int l, int r) {
int w = 0;
for(int i = l; i <= r; ++i) w = w * 10 + (ch[i] - '0');
return w;
}
void dfs(int x, int f) {
vis[x] = 1; dis[x] = (dis[f] * 1000000 % mod + x) % mod;
std::sort(eg[x].begin(), eg[x].end());
for(auto v : eg[x]) if(!vis[v]) dfs(v, x);
}
signed main() {
scanf("%d%d", &n, &m);
scanf("%s", ch+1);
for(int i = 1; i <= 12 * m; i += 12) {
int x = to_int(i, i+5), y = to_int(i+6, i+11);
eg[x].pb(y); eg[y].pb(x);
}
dfs(0, 0);
for(int i = 1; i < n; ++i) printf("%lld\n", !vis[i] ? -1ll : dis[i]);
return 0;
}
B LOJ #2743. 「JOI Open 2016」摩天大楼
麻了,这题为什么在第二题,卡常卡吐了。还忘了判 n=1
。
排下序,易得 dp:\(f_{i,j,k,0/1,0/1}\) 表示考虑前 \(i\) 个数,有 \(j\) 个连续段,费用为 \(k\),左/右的顶端有没有填掉。
这样 \(k\) 的枚举范围大概是个 \(2\sum a\),但实际上可以再紧一紧,然后滚动数组,用 vector
卡卡空间,就这样 \(\mathcal{O}(n^3a)\) 在LOJ上卡过去了...
优化到 \(\mathcal{O}(n^2L)\) 的话,考虑费用延后计算,这样的话向后转移 \(k\) 是不降的,\(k\) 的枚举范围就到 \(L\) 就可以了。
代码是 \(\mathcal{O}(n^3a)\) 的做法。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define pb emplace_back
typedef long long ll;
const ll mod = 1000000007;
ll Add(ll x, ll y) { return (x + y >= mod) ? (x + y - mod) : (x + y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return (x >= mod) ? (x - mod) : (x < 0 ? (x + mod) : x); }
inline ll cadd(int &x, ll y) { return x = (x + y >= mod) ? (x + y - mod) : (x + y); }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 101;
const int L = 1001;
int n, l, a[N], sum, all, lim;
int len[N][2][2];
std::vector<int> f[N][2][2], g[N][2][2];
int cmp(int x, int y) { return x > y; }
signed main() {
read(n); read(l);
if(n == 1) {
puts("1");
return 0;
}
for(int i = 1; i <= n; ++i) read(a[i]), all += a[i];
std::sort(a + 1, a + n + 1, cmp);
for(int i = 0; i <= n; ++i)
for(int p1 = 0; p1 <= 1; ++p1)
for(int p2 = 0; p2 <= 1; ++p2)
f[i][p1][p2].pb(0), f[i][p1][p2].pb(0),
g[i][p1][p2].pb(0), g[i][p1][p2].pb(0);
f[0][0][0][0] = 1;
for(int o = 1; o <= n; ++o) {
sum += a[o]; lim = Min(sum, l + 2 * (all - sum) + 2*a[o]);
for(int i = 0; i <= o; ++i)
for(int p1 = 0; p1 <= 1; ++p1)
for(int p2 = 0; p2 <= 1; ++p2)
while(len[i][p1][p2] <= 2*lim)
f[i][p1][p2].pb(0), g[i][p1][p2].pb(0), ++len[i][p1][p2];
for(int i = 0; i < o; ++i)
for(int j = 0; j <= 2*(lim-a[o]); ++j)
for(int p1 = 0; p1 <= 1; ++p1)
for(int p2 = 0; p2 <= 1; ++p2) {
if(!f[i][p1][p2][j]) continue ;
cadd(g[i+1][p1][p2][j+2*a[o]], 1ll * f[i][p1][p2][j] * (i+1-p1-p2) % mod); //新开一个连通块 且不在左/右
if(!p1) cadd(g[i+1][1][p2][j+a[o]], 1ll * f[i][p1][p2][j]); //新开一个连通块 在左
if(!p2) cadd(g[i+1][p1][1][j+a[o]], 1ll * f[i][p1][p2][j]); //新开一个连通块 在右
if(i) cadd(g[i][p1][p2][j], 1ll * f[i][p1][p2][j] * (2*i-p1-p2) % mod); //连上之前一个连通块 且不在左/右
if(!p1 && i) cadd(g[i][1][p2][j-a[o]], 1ll * f[i][p1][p2][j]); //连上之前一个连通块 在左
if(!p2 && i) cadd(g[i][p1][1][j-a[o]], 1ll * f[i][p1][p2][j]); //连上之前一个连通块 在右
if(i>=2) cadd(g[i-1][p1][p2][j-2*a[o]], 1ll * f[i][p1][p2][j] * (i-1) % mod);//连接两个联通块
}
for(int i = 0; i <= o; ++i)
for(int j = 0; j <= 2*lim; ++j)
for(int p1 = 0; p1 <= 1; ++p1)
for(int p2 = 0; p2 <= 1; ++p2)
f[i][p1][p2][j] = g[i][p1][p2][j],
g[i][p1][p2][j] = 0;
}
int ans = 0;
while(len[1][1][1] <= l) f[1][1][1].pb(0), ++len[1][1][1];
for(int i = 0; i <= Min(l, len[1][1][1]); ++i) {
cadd(ans, 1ll*f[1][1][1][i]);
}
printf("%d\n", ans);
return 0;
}
C LOJ #6723. 「CodePlus #7」教科书般的亵渎 / Luogu P5068 [Ynoi2015] 我回来了
麻了,本来是能切掉这个简单的Ynoi题的,但是看错 \(0\) 的个数,数组开小了...咋还能这样挂分的...
每个伤害值 \(d\) 可以分开考虑,那就考虑它在哪个时刻,对答案的贡献加了个 \(1\),这些总和是个调和级数 \(\mathcal{O}(n\log n)\) 的。
离线下来,对于每个值记录它最早在哪个时刻加入,用个线段树 / ST表查询区间最小值,对于每一个 \(d\) 暴力枚举 \([1,d],[d+1,2d]...\),然后看区间最小值,再做个前缀 \(\max\),就知道具体是在哪个时刻答案 \(+1\) 了。
最后统计答案的时候用个树状数组,支持单点 \(+1\),查询区间和就可以了,修改次数是 \(\mathcal{O}(n\log n)\),查询是 \(\mathcal{O}(m)\) 的。
总复杂度是 \(\mathcal{O}(n\log^2n+m\log n)\) 的。
#include<iostream>
#include<cstdio>
#include<vector>
#define pb push_back
typedef long long ll;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
inline int lowbit(int x) { return x & (-x); }
const int N = 1000010;
const int INF = 0x7fffffff;
int n, m;
int b[N];
std::vector<int>vec[N];
namespace Segment_Tree {
#define ls tree[x].lson
#define rs tree[x].rson
#define tl tree[x].l
#define tr tree[x].r
int trnt;
struct SGT {
int l, r, mn, lson, rson;
}tree[N << 1];
inline void pushup(int x) { tree[x].mn = Min(tree[ls].mn, tree[rs].mn); }
int build(int l, int r) {
int x = ++trnt; tl = l; tr = r;
if(l == r) {
tree[x].mn = b[l];
return x;
}
int mid = (l + r) >> 1;
ls = build(l, mid); rs = build(mid+1, r);
pushup(x);
return x;
}
int query(int x, int l, int r) {
if(tl >= l && tr <= r) return tree[x].mn;
int mid = (tl + tr) >> 1, sumq = INF;
if(mid >= l) sumq = Min(sumq, query(ls, l, r));
if(mid < r) sumq = Min(sumq, query(rs, l, r));
return sumq;
}
#undef ls
#undef rs
#undef tl
#undef tr
}
using namespace Segment_Tree;
int opt[N], l[N], r[N], h[N], t[N];
int tr[N];
void modify(int x, int v) { for(; x <= n; x += lowbit(x)) tr[x] += v; }
int query(int x) { int sumq = 0; for(; x; x -= lowbit(x)) sumq += tr[x]; return sumq; }
int qsum(int l, int r) { return query(r) - query(l-1); }
signed main() {
read(n); read(m);
for(int i = 1; i <= n; ++i) b[i] = INF;
for(int i = 1; i <= m; ++i) {
read(opt[i]);
if(opt[i] == 1) read(h[i]), b[h[i]] = Min(b[h[i]], i);
else read(l[i]), read(r[i]);
}
build(1, n);
for(int i = 1; i <= n; ++i) {
for(int j = 1; (j-1)*i < n; ++j) {
t[j] = query(1, (j-1)*i+1, Min(j*i, n));
if(t[j] == INF) break ;
}
for(int j = 1; (j-1)*i < n; ++j) {
if(t[j] == INF) break ;
t[j] = Max(t[j], t[j-1]);
vec[t[j]].pb(i);
}
}
for(int i = 1; i <= m; ++i) {
for(auto x : vec[i]) modify(x, 1);
if(opt[i] == 2) {
printf("%d\n", r[i]-l[i]+1 + qsum(l[i], r[i]));
}
}
return 0;
}
D LOJ #2350. 「JOI 2018 Final」月票购买
不会做,被打爆了...
考虑找出在 \(S\to T\) 最短路上的边,形成个 DAG(正反方向都可以),两种方向分别考虑。
结论是 \(U\to V\) 的路径如果和 \(S\to T\) 的最短路相交,则仅有一段相交,因为如果有两段的话,显然中间断开的路走 \(S\to T\) 的最短路更优。
那就在 DAG 上拓扑排序 / dfs,对于每个点记录能走到它的点(或者它能走到的点)的 \(dis(U,y),dis(V,y)\) 的最小值,分别记作 \(f_x,g_x\),答案即为 \(\min\{f_x+dis(V,x),g_x+dis(U,x),dis(U,V)\}\).
#include<iostream>
#include<cstdio>
#include<queue>
#define mp std::make_pair
#define fir first
#define sec second
typedef long long ll;
typedef std::pair<ll, int> pli;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 100010;
const ll INF = 0x7fffffffffffffff;
int n, m, S, T, U, V;
int ent = 1, head[N], pre[N];
ll dS[N], dT[N], dU[N], dV[N], f[N], g[N], ans;
bool vis[N];
struct Egde {
int to, val, nxt;
}e[N<<2];
inline void add(int x, int y, int z) {
e[++ent].to = y; e[ent].val = z; e[ent].nxt = head[x]; head[x] = ent;
}
void Dij(int s, ll *dis) {
for(int i = 1; i <= n; ++i) vis[i] = 0, dis[i] = INF;
dis[s] = 0;
std::priority_queue<pli>q;
q.push(mp(0, s));
while(!q.empty()) {
int x = q.top().sec; q.pop();
if(vis[x]) continue ;
vis[x] = 1;
for(int i = head[x]; i; i = e[i].nxt) {
int v = e[i].to;
if(dis[v] > dis[x] + e[i].val) {
dis[v] = dis[x] + e[i].val;
q.push(mp(-dis[v], v));
}
}
}
}
void dfs(int x) {
if(vis[x]) return ;
vis[x] = 1; f[x] = dU[x]; g[x] = dV[x];
for(int i = head[x]; i; i = e[i].nxt) {
int v = e[i].to;
if(dS[x] + dT[v] + e[i].val != dS[T]) continue ;
dfs(v);
f[x] = Min(f[x], f[v]); g[x] = Min(g[x], g[v]);
}
ans = Min(ans, Min(f[x] + dV[x], g[x] + dU[x]));
}
signed main() {
read(n); read(m);
read(S); read(T);
read(U); read(V);
for(int i = 1; i <= m; ++i) {
int u, v, w; read(u); read(v); read(w);
add(u, v, w);
add(v, u, w);
}
Dij(S, dS); Dij(T, dT); Dij(U, dU); Dij(V, dV); ans = INF;
for(int i = 1; i <= n; ++i) f[i] = g[i] = 0x3f3f3f3f3f3f3f3f, vis[i] = 0;
dfs(S);
ans = Min(ans, dU[V]);
printf("%lld\n", ans);
return 0;
}