CSP-S模拟2(联考)

差点又双叒叕模拟退役

上来先\(\% T2\), 然后感觉就差一点, 最后搞出来就十点多了..

然后心态一度爆炸,有点小摆烂,上厕所冷静一下觉得还有时间,能抢救一下

然后开了 \(T1\), 没啥思路瞎搞,开根号在附近找数用 \(pow\),发现大数据丢精异常严重 $pow(n, 1.0 / 1) $比 \(n\)大了好几百你敢信, 看一下时限\(6s\),于是手写二分, 然后打挂了一堆细节,调完就 \(11:22\)

\(T3\), 感谢\(T3\),上来想到主席树二分直接莽, 然后一遍码对了? \(11:44\)拿到 \(95pts\)(主席树空间不对),防止了模拟退役

\(T4\)打了个\(DP\),显然没打过,不过离正解就差了亿点

感觉上来搞 \(T2\) 策略有点问题,还好切掉了,并且稳住了心态

A. 谜之阶乘

枚举相邻的数的个数\(i\),然后在\(^i\sqrt n\)左右找数, \(pow\)精度有问题,手写二分,但是数很大会炸 \(long long\), 所以先判 \(n / now >= mid\)\(now = now * mid\)

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = -1;
ll fac[55];
void init(){
	fac[0] = 1;
	for(int i = 1; i <= 20; ++i)fac[i] = fac[i - 1] * i;
}
inline ll read(){
	ll x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
ll a[105], b[105];
ll mr(ll n, int i){
	ll l = 1, r = n, ans = 1;
	while(l <= r){
		ll mid = (l + r) >> 1;
		ll now = 1; bool f = 1;
		for(int j = 1; j <= i; ++j){
			if(n / now >= mid)now = mid * now;
			else now = n + 1;
			if(now >= n || now < 0){
				r = mid - 1; ans = mid;
				f = 0; break;
			}
		}
		if(f)l = mid + 1;
	}
	return ans;
}
void solve(){
	ll n = read();
	if(n == 1){printf("-1\n");return;}
	int cnt = 0, mx = 0;
	for(int i = 1; i <= 20; ++i)if(fac[i + 1] > n){mx = i; break;}
	if(fac[mx] == n){
		++cnt;
		a[cnt] = mx;
		b[cnt] = 1;
	}
	for(int i = mx - 1; i > 0; --i){
		ll r = mr(n, i);
		ll l = max(3ll, r - i - i - i);
		for(ll j = l; j <= r; ++j){
			ll now = 1;
			for(ll p = 0; p < i; ++p){
				now = now * (j + p);
				if(now > n)break;
				if(now < 0)break;
			}
			if(now == n){
				++cnt;
				a[cnt] = j + i - 1;
				b[cnt] = j - 1;
				break;
			}
		}
	}
	printf("%d\n",cnt);
	for(int i = 1; i <= cnt; ++i)printf("%lld %lld\n",a[i], b[i]);
}
int main(){
	init();
	int t = read();
	for(int ask = 1; ask <= t; ++ask)solve();
	return 0;
}

B. 子集

构造很有意思,但是很费时间,很是人类智慧,所以下次。。。

还是先试试逝世

\(sum = n * (n - 1) / 2\)

\(k \nmid sum\) 或者 \(sum / k < n\)无解

\(n/ k\) 为偶数的话对应首尾项相加,然后得到 \(n/ 2\)个和相同的二元组,随便分一下就行

对于 \(n / k\) 为奇数的,每\(k\)项写成一行,每行的 \(k\) 个分入不同的集合里

定义这\(k\)列的权值为 \(- k / 2, -k/2 + 1 ...... 0 ...... k/ 2 - 1, k / 2\)

特殊处理前三行,后面的\(Z\)字形,(就是 \(x, -x, x , -x\)这样取权值)

对于前三行,先考虑前两行,我们先强制 \(x, -x\)配对, 这样第三行需要的都是 \(0\)

考虑对 \((x, -x) (y, -y)\)交换为 \((x, -y)(y, -x)\)那么他们需要的第三行就是 \(|x - y|, -|x - y|\)

那么我们只需要交换距离为 \(1, 2 .... (k + 1)/ 2\) 的不同的二元组的第二项,发现可以对应\(reverse[1, (k + 1)/ 2][(k + 1)/ 2 +1, k]\)两个区间对应的第二行元素即可

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
ll n, k, sum;
int ls[1000005];
void nosolution(){printf("No\n");}
void solve(){
	if(k == 1){
		printf("Yes\n");
		for(int i = 1; i <= n; ++i)printf("%d ",i);printf("\n");return;
	}
	sum = 1ll * n * (n + 1) / 2;
	if(sum % k)return nosolution();
	if(sum / k < n) return nosolution();
	if((n / k) & 1){
		printf("Yes\n");
		int s = (1 + k * 3) * (k * 3) / 2 / k;
		for(int i = 1; i <= k; ++i)ls[i] = k + k + 1 - i;
		reverse(ls + 1, ls + (k + 1) / 2 + 1);
		reverse(ls + (k + 1) / 2 + 1, ls + k + 1);
		for(int i = 1; i <= k; ++i){
			printf("%d %d %d ",i, ls[i], s - i - ls[i]);
			int dt = (i + 1) / 2, f = i % 2;
			for(int j = 4; j <= n / k; ++j){
				if(f)printf("%lld ", (j - 1) * k + dt);
				else printf("%lld ",j * k - dt + 1);
				f = 1 - f;
			}
			printf("\n");
		}
		return;
	}else{
		printf("Yes\n");
		int cnt = 1;
		for(int i = 1; i <= k; ++i){
			for(int j = n / k / 2; j; --j){
				printf("%d %lld ",cnt, n - cnt + 1);
				++cnt;
			}
			printf("\n");
		}
		return;
	}

}
int main(){
	int t = read();
	for(int ask = 1; ask <= t; ++ask){
		n = read(), k = read();
		solve();
	}
	return 0;
}

C. 混凝土粉末

主席树 + 标记永久化 + 二分 + \(22min\)= \(95pts\)

得分效率奇高

95pts
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 1000005;

inline ll read(){
	ll x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, q;
int root[maxn], tot;
struct tree{
	struct node{
		int l, r; ll val;
	}t[maxn * 40];
	int cnt;
	void insert(int &x, int hrt, int l, int r, int L, int R, ll hi){
		if(!x) x = ++cnt, t[x] = t[hrt];
		if(L <= l && r <= R){
			t[x].val += hi;
			return;
		}
		int mid = (l + r) >> 1;
		if(L <= mid){
			t[x].l = 0;
			insert(t[x].l = 0, t[hrt].l, l, mid, L, R, hi);
		}
		if(R > mid){
			t[x].r = 0;
			insert(t[x].r, t[hrt].r, mid + 1, r, L, R, hi);
		}
	}
	ll query(int x, int l, int r, int pos){
		if(!x)return 0;
		if(l == r)return t[x].val;
		int mid = (l + r) >> 1;
		if(pos <= mid)return t[x].val + query(t[x].l, l, mid, pos);
		else return t[x].val + query(t[x].r, mid + 1, r, pos);
	}
}t;
int bound(int r){
	int x = read(), l = 1; ll y = read();
	if(t.query(root[r], 1, n, x) < y)return 0;
	int ans = r;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(t.query(root[mid], 1, n, x) >= y)ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	return ans;
}
int main(){
	n = read(), q = read();
	for(int i = 1; i <= q; ++i){
		int op = read();
		if(op & 1){
			int l = read(), r = read();
			ll h = read();
			t.insert(root[i], root[i - 1], 1, n, l, r, h);
		}else{
			root[i] = root[i - 1];
			printf("%d\n",bound(i));
		}
	}
	return 0;
}

正解扫描线,对每个位置分开二分,用树状数组维护每个时间对应的高度

对应修改\(time, l, r,h\)\(l\)位置对\(time\)时刻 \(+h\), \(r + 1\) 位置对 \(time\)时刻 \(-h\)这样

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>

using namespace std;

typedef long long ll;
const int maxn = 1000005;

inline ll read(){
	ll x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, q;

struct tree{
	ll t[maxn];
	int lowbit(int x){return x & -x;}
	void add(int x, ll h){
		while(x <= q){
			t[x] += h;
			x += lowbit(x);
		}
	}
	ll query(int x){
		ll ans = 0;
		while(x){
			ans += t[x];
			x -= lowbit(x);
		}
		return ans;
	}
}t;
struct node{int time; ll val;};
vector<node>modify[maxn], query[maxn];
int ans[maxn], tot;
bool flag[maxn];
int bound(node x){
	if(t.query(x.time) < x.val)return 0;
	int l = 1, r = x.time;
	int ans = r;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(t.query(mid) >= x.val)ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	return ans;
}
int main(){
	n = read(), q = read();
	for(int i = 1; i <= q; ++i){
		int op = read();
		if(op & 1){
			int l = read(), r = read();
			ll h = read();
			modify[l].push_back({i, h});
			modify[r + 1].push_back({i, -h});
		}else{
			int x = read();
			query[x].push_back({i, read()});
			flag[i] = 1;
		}
	}
	for(int i = 1; i <= n; ++i){
		for(node x : modify[i])t.add(x.time, x.val);
		for(node x : query[i])ans[x.time] = bound(x);
	}
	for(int i = 1; i <= q; ++i)if(flag[i])printf("%d\n",ans[i]);
	return 0;
}

D. 排水系统

注:\(cd_{x}\) 表示出度

\(g_{x, 0}\)表示推到 \(x\)的原流(没有损坏), \(g_{x, 1}\)表示期望流

在拓扑过程中,可以以均等概率直接推送

然后考虑如果断掉了当前点的某条出边对其他点的贡献

断边概率 \(a/sum\) 断边对其他边流量的影响\(g_{x, 0} / cd[x]/(cd[x] - 1)\)

把该点出边的这部分 \(\sum\) 起来,然后再扫一遍,减去自己的就是其他断边对该点新推流的贡献,还有 \(a/sum\) 的概率断该边需要减去 \(g_{x,0}/cd[x]\)

感谢 \(Delov\) 大佬的巨大帮助

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 200005;
const int maxm = 500005;
const int mod = 998244353;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
struct edge{
	int to, net, val;
}e[maxm];
int head[maxn], tot;
void add(int u, int v, int w){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].val = w;
}
int n, m, r, k, inv;
int g[maxn][2], rd[maxn], cd[maxn], iv[maxm];
queue<int>q;
signed  main(){
	n = read(), m = read(), r = read(), k = read();
	iv[1] = 1;
	for(int i = 2; i <= k; ++i)iv[i] = 1ll * (mod - mod / i) * iv[mod % i] % mod;
	int suma = 0;
	for(int i = 1; i <= k; ++i){
		int u = read(), v = read(), w = read();
		add(u, v, w); suma = (suma + w) % mod;
		++rd[v]; ++cd[u];
	}
	inv = qpow(suma, mod - 2);
	for(int i = 1; i <= n; ++i)if(!rd[i])q.push(i);
	for(int i = 1; i <= m; ++i)g[i][0] = 1, g[i][1] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		int sum = 0;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			--rd[v]; if(rd[v] == 0)q.push(v);
			g[v][1] = (g[v][1] + 1ll * g[x][1] * iv[cd[x]] % mod) % mod;
			g[v][0] = (g[v][0] + 1ll * g[x][0] * iv[cd[x]] % mod) % mod;
			sum = (sum + 1ll * g[x][0] * inv % mod * e[i].val % mod * iv[cd[x] - 1] % mod * iv[cd[x]]) % mod;
		}
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			g[v][1] = (g[v][1] + sum - 1ll * g[x][0] * inv % mod * e[i].val % mod * iv[cd[x] - 1]  % mod * iv[cd[x]] % mod + mod) % mod;
			g[v][1] = (g[v][1] + mod - 1ll * g[x][0] * inv % mod * e[i].val % mod * iv[cd[x]] % mod) % mod;
		}
	}
	for(int i = n - r + 1;  i <= n; ++i)printf("%d ",g[i][1]);
	return 0;
}
posted @ 2022-09-04 16:51  Chen_jr  阅读(101)  评论(3编辑  收藏  举报