CSP-S模拟2
都说T1T2是大水题,我鹤完题解之后还感觉奇奇妙妙收获满满,这就是差距吗
A. 谜之阶乘
TLE:要用两个阶乘的差算出n,就是把n用几个连续的数相乘表示出来,这几个连续的数都应该是n的因数,于是可以先把因数找到,再枚举以每个因数作为这几个连续的数相乘的右边界,往前循环的时候判断是否相等和是否连续。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = -2; const int N = 60; const ll mod = 1e9 + 7; int T, sq; ll n, sz; vector<ll> yin; vector<pair<ll, ll> > ans; inline ll read() { ll x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { //freopen("factorial2.in", "r", stdin); //freopen("ww.txt", "w", stdout); T = read(); while(T--) { n = read(); ans.clear(); yin.clear(); if(n == 1) { printf("-1\n"); continue; } sq = sqrt(n); //printf("sq = %d\n", sq); yin.push_back(n); //if(sq * sq == n) yin.push_back(sq); for(int i=2; i<=sq; i++) { if(n % i == 0) { yin.push_back(i); if(i * i == n) break; yin.push_back(n/i); } } sort(yin.begin(), yin.end()); sz = yin.size(); /*for(ll i=0; i<sz; i++) { printf("%lld ", yin[i]); } printf("\n");*/ for(ll i=0; i<sz; i++) { ll now = yin[i]; if(now == n) { ans.push_back(make_pair(yin[i], yin[i]-1)); } for(int j=i-1; j>=0; j--) { if(yin[j]+1 == yin[j+1]) { now *= yin[j]; } else break; if(now == n) { ans.push_back(make_pair(yin[i], yin[j]-1)); break; } } } sort(ans.begin(), ans.end()); sz = ans.size(); printf("%lld\n", sz); for(ll i=0; i<sz; i++) { printf("%lld %lld\n", ans[i].first, ans[i].second); } } return 0; }
正解:枚举a和b的差,a只有d种选择是一件奇妙的事,如果从1开始找a把不满足条件的b跳过是62分。
这个a和b恰好和题面上的换了个位置……

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = -2; const int N = 60; const ll mod = 1e9 + 7; int T; ll n, sz; vector<pair<ll, ll> > ans; inline ll read() { ll x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } inline bool check(ll l, ll r) { ll res = 1; for(ll i=l; i<=r; i++) { res *= i; if(res < 0) return 0; } if(res != n) return 0; return 1; } int main() { //freopen("factorial2.in", "r", stdin); //freopen("ww.txt", "r", stdin); T = read(); while(T--) { n = read(); ans.clear(); //yin.clear(); if(n == 1) { printf("-1\n"); continue; } ans.push_back(make_pair(n, n-1)); for(int d=2; d<=20; d++) { ll Max = pow(n, 1.0/d); //printf("Max = %lld\n", Max); for(ll a=max(Max-d,1ll),b=a+d; a<=Max; a++,b++) { //printf("pair: %lld %lld\n", b, a); if(check(a+1, b)) ans.push_back(make_pair(b, a)); } } sort(ans.begin(), ans.end()); sz = ans.size(); printf("%lld\n", sz); for(ll i=0; i<sz; i++) { printf("%lld %lld\n", ans[i].first, ans[i].second); } } return 0; }
B. 子集
几乎每个点的开头都是一个n=1,k=1!?真是够坑的,特判n=k输出No会挂不少分。
我借鉴的题解有大字提示可惜我没带眼睛……
qes1:为什么n是偶数m(表示长度m=n/k)是奇数时无解?
把n = m * k带入求和公式——sum = (1+m*k)*m*k / 2; 如果有解要求sum是k的倍数,这样每个组的和为(i+m*k)*m / 2. n = m*k是偶数m是奇数,所以分子是奇数,然后它除不尽了。
qes2:还是直接CV好了**%%%雪域亡魂

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 2; const int N = 60; const ll mod = 1e9 + 7; int T, n, k, m, p; vector<int> a[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { T = read(); while(T--) { n = read(); k = read(); if(n == k && n != 1) { printf("No\n"); continue; } if(k == 1) { printf("Yes\n"); for(int i=1; i<=n; i++) { printf("%d ", i); } printf("\n"); continue; } m = n / k; if(!(n & 1)) { if(m & 1) { printf("No\n"); continue; } printf("Yes\n"); int l = 1, r = n; while(l < r) { int cnt = 0; while(cnt != m) { printf("%d %d ", l, r); cnt += 2; l++; r--; } printf("\n"); } } else { printf("Yes\n"); int tmp = 0, mid = (1 + k) >> 1; for(int i=1; i<=k; i++) { tmp++; a[i].push_back(tmp); } for(int i=mid+1; i<=k; i++) { tmp++; a[i].push_back(tmp); } for(int i=1; i<=mid; i++) { tmp++; a[i].push_back(tmp); } tmp++; a[mid].push_back(tmp); int tot = 0; for(int i=0; i<=2; i++) tot += a[mid][i]; for(int i=1; i<=k; i++) { if(i != mid) a[i].push_back(tot-a[i][0]-a[i][1]); } tmp = 3*k; for(int i=4; i<=m; i++) { if(i & 1) { for(int j=1; j<=k; j++) { tmp++; a[j].push_back(tmp); } } else { for(int j=k; j>=1; j--) { tmp++; a[j].push_back(tmp); } } } for(int i=1; i<=k; i++) { for(int j=0; j<m; j++) { printf("%d ", a[i][j]); } printf("\n"); } for(int i=1; i<=k; i++) { a[i].clear(); } } } return 0; }
C. 混凝土粉末
非常不良心,我开了一个vector并循环模拟往上堆粉末,开大了MLE,开小了RE,总之就都是0000
转化1:做若干次区间加,询问x位置上最早>=y的时间。
为了检验一下我的rp,就去鹤了一下那个(80~100)不等的主席树,结果Copy怎么可能比原版还多呢MLE95,%%%Chen_jr
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 2; const int N = 60; const ll mod = 1e9 + 7; int n, q, root[maxn], tot; inline ll read() { ll x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } 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, 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; inline int solve(int r) { int x = read(), l = 1; ll y = read(); if(t.query(root[r], 1, n, x) < y) return 0; while(l < r) { int mid = (l + r) >> 1; if(t.query(root[mid], 1, n, x) >= y) r = mid; else l = mid + 1; } return l; } int main() { //freopen("concrete2.in", "r", stdin); n = read(); q = read(); for(int i=1; i<=q; i++) { int tp = read(); if(tp == 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", solve(i)); } } return 0; }
转化2:离线问题,把每个位置单独考虑,对于这个位置上的问题就在当前位置上二分,少了一堆多余存储。为了从一个位置走到另一个位置,可以把区间修改的贡献写成先加后减的形式。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 2; const int N = 60; const ll mod = 1e9 + 7; inline ll read() { ll x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int n, q, ans[maxn]; bool flag[maxn]; struct node { int time; ll val; }; vector<node> modify[maxn], query[maxn]; 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; int solve(node x) { if(t.query(x.time) < x.val) return 0; int l = 1, r = x.time; while(l < r) { int mid = (l + r) >> 1; if(t.query(mid) >= x.val) r = mid; else l = mid + 1; } return l; } int main() { //freopen("concrete2.in", "r", stdin); n = read(); q = read(); for(int i=1; i<=q; i++) { int tp = read(); if(tp == 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] = solve(x); } for(int i=1; i<=q; i++) { if(flag[i]) { printf("%d\n", ans[i]); } } return 0; }
D. 排水系统
暴力只搞到了13分这么个不整齐的数字,它有错的所以借鉴意义不大,犹豫了一下还是不放了。
Chen_jr写得太好了言简意赅思路清晰...(Words failed me. The last few minutes saw me wolfing down his code as if it was the best food in the world. My heart was filled with excitement.)以至于我又有点lazy

至于对其它边流量影响的那个式子,就是用g[x][0] / (cd[x]-1) - g[x][0] / cd[x] 通分化简即可,我居然还看了半天没看懂***
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 6; const int N = 5e5 + 2; const ll mod = 998244353; int n, m, r, k, inv, g[maxn][2], cd[maxn], iv[N], rd[maxn]; queue<int> q; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } ll qpow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } struct node { int next, to, val; }a[N]; int head[maxn], len; void add(int x, int y, int w) { a[++len].to = y; a[len].next = head[x]; a[len].val = w; head[x] = len; } int 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=a[i].next) { int v = a[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*a[i].val%mod*iv[cd[x]-1]%mod*iv[cd[x]])%mod; } for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; g[v][1] = (g[v][1]+sum-1ll*g[x][0]*inv%mod*a[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*a[i].val%mod*iv[cd[x]]%mod)%mod; } } for(int i=n-r+1; i<=n; i++) { printf("%d ", g[i][1]); } return 0; }

浙公网安备 33010602011771号