2019杭电多校第三场

编程入门 行业动态 更新时间:2024-10-25 04:18:10

2019杭电多校<a href=https://www.elefans.com/category/jswz/34/1589167.html style=第三场"/>

2019杭电多校第三场

1004:Distribution of books

求最大值的最小值,明晃晃的告诉我们用二分。二分答案,然后考虑怎么check
设当前检查答案lim是否合法:
用dp来检验。 d p [ i ] dp[i] dp[i]表示[1,i]这个区间在满足每个小朋友的愉悦值不超过lim的情况下最多可以分给几个小朋友。那么转移为:
d p [ i ] = m a x ( d p [ j ] + 1 ) dp[i]=max(dp[j]+1) dp[i]=max(dp[j]+1), j j j需要满足的条件为: s u m [ i ] − s u m [ j ] &lt; = l i m sum[i]-sum[j]&lt;=lim sum[i]−sum[j]<=lim,即 s u m [ j ] &gt; = s u m [ i ] − l i m sum[j]&gt;=sum[i]-lim sum[j]>=sum[i]−lim。sum数组为前缀和数组。
直接转移的复杂度是 n 2 n^2 n2的,所以考虑用线段树优化。
对于每个 d p [ i ] dp[i] dp[i],它是由满足 s u m [ j ] &gt; = s u m [ i ] − l i m sum[j]&gt;=sum[i]-lim sum[j]>=sum[i]−lim的最大的 d p [ j ] dp[j] dp[j]转移来的。我们要找到这个满足条件的 d p [ j ] dp[j] dp[j]。
所以对于线段树上的点 x x x,维护一个使得 d p [ j ] = x dp[j]=x dp[j]=x的最大的 s u m [ j ] sum[j] sum[j]。对 d p [ i ] dp[i] dp[i]转移的时候,只需要找到线段树上存储的值大于等于 s u m [ i ] − l i m sum[i]-lim sum[i]−lim的最大下标即可。
ac代码:

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e5 + 50;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, k;
ll a[maxn];
ll sum[maxn];
ll mx[maxn<<2];
void up(int rt){mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
}
void build(int rt, int l, int r)
{mx[rt] = -inf;if(l == r) return;build(lson);build(rson);
}
void add(int rt, int l, int r, int i, ll val){if(l == r){mx[rt] = max(mx[rt], val);return;}if(i <= mid) add(lson, i, val);else add(rson, i, val);up(rt);
}
int query(int rt, int l, int r, ll val){//在l到r寻找一个最大的满足mx[i] >= val的iif(mx[rt] < val) return -1;if(l == r) return l;int ans = query(rson, val);if(ans == -1) ans = query(lson, val);return ans;
}
void init()
{scanf("%d%d", &n, &k);for(int i = 1; i <= n; ++i) {scanf("%lld", &a[i]);sum[i] = sum[i-1] + a[i];}
}
int dp[maxn];
bool check(ll lim)
{build(1, 1, k);for(int i = 1; i <= n; ++i) {dp[i] = 0;if(sum[i] <= lim) dp[i] = 1;int t = query(1, 1, k, sum[i] - lim);dp[i] = max(dp[i], t + 1);if(dp[i] >= k) return true;if(dp[i] > 0) add(1, 1, k, dp[i], sum[i]);}return false;
}
void sol()
{ll t = 1e9 + 7;ll l = -t*n, r = t*n;ll ans;while(l <= r){if(check(mid)) {ans = mid;r = mid - 1;}else l = mid + 1;}printf("%lld\n", ans);
}
int main()
{int T;cin>>T;while(T--){init();sol();}
}
1006: Fansblog

由于素数之间的间隔不会太大,所以可以直接向下枚举找到相邻的素数(用Miller Rabbin判断是否素数)。
打表发现对于一个素数P,有(P-1)!%P = P-1,然后就反向挨个乘逆元求得答案了。(数学上来先打表

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<time.h>
#define ll long long
using namespace std;
ll mul(ll a, ll b, ll m){ll res = 0;while(b){if(b&1) res =(res + a)%m;a = (a + a) % m;b >>= 1;}return res;
}
ll qm(ll a, ll b, ll m){ll res = 1;a%=m;while(b){if(b&1) res = mul(res,a,m);a = mul(a,a,m);b>>=1;}return res;
}
ll gcd(ll a, ll b){if(b == 0) return a;while(a%b){ll t = a%b;a = b; b = t;}return b;
}
bool check(ll n, int repeat){if(n < 2) return false;if(n == 2 || n == 3) return true;if(n%2 == 0) return false;ll d = n-1;int s = 0;while(!(d&1)) s++, d>>=1;//x - 1 = 2^s * d;//srand((unsigned)time(NULL));while(repeat--){ll a = (ll)rand()%(n-1) + 1;ll x = qm(a,d,n);ll y = 0;for(int j = 0; j < s; ++j){y = mul(x, x, n);if(y == 1 && x != 1 && x != n-1) return false;x = y;}if(y!=1) return false;}return true;
}
ll pollard_rho(ll n, int c){// srand((unsigned)time(NULL));ll x = rand()%(n-1) + 1;ll y = x, i = 1, k = 2;while(1){i++;x = (mul(x, x, n) + c)%n;ll d = gcd((x - y + n)%n, n);if(d > 1 && d < n) {return d;}if(x == y) {return n;}if(i == k) {y = x; k <<= 1;}}
}
ll pri[500];
int cnt = 0;
void fnd(ll n, int c){if(n == 1) return;if(check(n, 20)){pri[cnt++] = n;return;}int k = c;ll p = n;while(p == n) p = pollard_rho(n, c--);fnd(p, k);fnd(n/p, k);
}
bool is_prime(ll n){return check(n, 20);
}
int main(){int T;cin>>T;while(T--){ll P;scanf("%lld", &P);ll Q;for(Q = P-2; Q > 0; Q-=2){if(is_prime(Q)) break;}ll ans = 1;for(ll i = P-2; i > Q; --i){ll inv = qm(i, P-2, P);ans = mul(ans, inv, P);}printf("%lld\n", ans);}return 0;
}
1007: Find the answer

问题等价于m-a[i]容量的背包最多放入a[1]…a[i-1]中几个数。贪心,从小到大放。直接暴力显然超时,离散化后线段树维护区间数字个数和区间数字和,左子树全可以放就全放然后搜右子树,否则递归左子树。注意行末要加空格
(头一次看到需要加行末空格的输出格式

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e5 + 50;
int n;
ll m;
ll sum[maxn<<2], sz[maxn<<2];
ll w[maxn], cc[maxn];
int num;
void build(int rt, int l, int r)
{sum[rt] = sz[rt] = 0;if(l == r) return;build(lson);build(rson);
}
void add(int rt, int l, int r, int i){if(l == r) {sz[rt] ++;sum[rt] = sum[rt] + cc[i];return;}if(i <= mid) add(lson, i);else add(rson, i);sum[rt] = sum[rt<<1] + sum[rt<<1|1];sz[rt] = sz[rt<<1] + sz[rt<<1|1];return;
}
int query(int rt, int l, int r, ll &res){if(sum[rt] <= res){res -= sum[rt];return sz[rt];}if(cc[l] > res) return 0;if(l == r){int ans = res / cc[l];res = res - ans*cc[l];return ans;}int ans = 0;ans += query(lson, res);ans += query(rson, res);return ans;
}
void init(){scanf("%d%lld", &n, &m);num = 0;for(int i = 1; i <= n; ++i){scanf("%lld", &w[i]);cc[++num] = w[i];}sort(cc+1,cc+1+num);num = unique(cc+1, cc+1+num) - cc - 1;build(1, 1, num);
}
int ans[maxn];
void sol(){for(int i = 1; i <= n; ++i){if(sum[1] <= m - w[i]) ans[i] = 0;else{ll res = m - w[i];int t = query(1, 1, num, res);//printf("%d", i-1-t);ans[i] = i-1-t;}int pos = lower_bound(cc+1,cc+1+num,w[i]) - cc;add(1, 1, num, pos);}for(int i = 1; i <= n; ++i){printf("%d ", ans[i]);}printf("\n");
}
int main()
{int T;cin>>T;while(T--){init();sol();}
}

更多推荐

2019杭电多校第三场

本文发布于:2024-02-25 15:02:03,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1699365.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:第三场   杭电多校

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!