ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CSP-S模拟2(联考)

2022-09-04 17:01:02  阅读:139  来源: 互联网

标签:int ll mid long CSP maxn include 联考 模拟


差点又双叒叕模拟退役

上来先\(\% 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. 排水系统

\(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;
}

标签:int,ll,mid,long,CSP,maxn,include,联考,模拟
来源: https://www.cnblogs.com/Chencgy/p/16655420.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有