do_while_true

一言(ヒトコト)

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;
}
posted @ 2021-09-25 15:07  do_while_true  阅读(70)  评论(1编辑  收藏  举报