《2019 Multi-University Training Contest 3》
Fansblog:
虽然想到了威尔逊定理,但是觉得用不上。
这里有个结论,那就是相邻的质数之间的最大距离 <= 1e5 + 5。
有了这个结论,我们就能暴力找到Q。
然后根据威尔逊定理 (p - 1)! mod p = p - 1。
那么(p - 1) ! = (p - 1) * (p - 2) * .... Q * ... 1= p - 1.
所以Q! = (p - 1) / ((p - 1) * (p - 2) * ... (Q + 1) )
但是这里因为p最大为1e14,所以单纯乘法可能会爆longlong。
所以对于每个乘法都要用快速乘。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 3e5 + 5; const int M = 1e6 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; bool check(LL x) { LL m = sqrt(x); for(int i = 2;i <= m;++i) { if(x % i == 0) return false; } return true; } LL quick_c(LL a,LL b,LL mod) { LL re = 0; while(b) { if(b & 1) re = (re + a) % mod; b >>= 1; a = (a << 1) % mod; } return re; } LL quick_mi(LL a,LL b,LL mod) { LL re = 1; while(b) { if(b & 1) re = quick_c(re,a,mod); a = quick_c(a,a,mod); b >>= 1; } return re; } int main() { int ca;scanf("%d",&ca); while(ca--) { LL p;scanf("%lld",&p); LL ans = p - 1,up; for(LL i = p - 1;;--i) { if(check(i)) { up = i; break; } } for(LL i = up + 1;i <= p - 1;++i) ans = quick_c(ans,quick_mi(i,p - 2,p),p); printf("%lld\n",ans); } // system("pause"); return 0; }
Find the answer:
这题个人觉得比较直白,很显然最优的取法是排序之后取最大的前缀和。
那么这点我们可以建一棵类似权值树的树,上面的节点的值代表排序后的a[L]。
然后每次查询区间最大的和即可。
注意的是这里不能对值离散化,也就是相同的点要看成不同的,这样方便计算个数。
但是这题的输出逆天了,必须要每个后面都带一个空格,不然就报pe...
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 2e5 + 5; const int M = 4e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int a[N],b[N],ans[N]; struct QT{int id,x;}p[N]; struct Node{int L,r,num;LL sum;}node[M << 2]; bool cmp(QT a,QT b) { return a.x < b.x; } void Pushup(int idx) { node[idx].sum = node[idx << 1].sum + node[idx << 1 | 1].sum; node[idx].num = node[idx << 1].num + node[idx << 1 | 1].num; } void build(int L,int r,int idx) { node[idx].L = L,node[idx].r = r,node[idx].sum = node[idx].num = 0; if(L == r) { return ; } int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); } void update(int x,LL val,int idx) { if(node[idx].L == node[idx].r) { node[idx].sum = val; node[idx].num = 1; return ; } int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= x) update(x,val,idx << 1); else update(x,val,idx << 1 | 1); Pushup(idx); } int query(LL up,int idx) { if(node[idx].sum <= up) { return node[idx].num; } int num = 0; if(node[idx << 1].sum <= up) { num += node[idx << 1].num; num += query(up - node[idx << 1].sum,idx << 1 | 1); } else num += query(up,idx << 1); return num; } int main() { int ca;scanf("%d",&ca); while(ca--) { int n,m;scanf("%d %d",&n,&m); for(int i = 1;i <= n;++i) scanf("%d",&a[i]),p[i].id = i,p[i].x = a[i]; sort(p + 1,p + n + 1,cmp); for(int i = 1;i <= n;++i) b[p[i].id] = i; build(1,n,1); for(int i = 1;i <= n;++i) { int ma = query(m - a[i],1); ans[i] = i - 1 - ma; update(b[i],a[i],1); } for(int i = 1;i <= n;++i) printf("%d ",ans[i]); printf("\n"); } //system("pause"); return 0; }
K Subsequence:
这题一直想的是怎么dp,居然是用费用流做的。
但是建模之后确实就很好理解了。
这里的一个问题就是普通费用流会被卡时间。
先把spfa换成dij,复杂度依旧不够,要再上原始对偶优化才行。
/ Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 4e3 + 5; const int M = 4e6 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int a[N]; struct edge { int to, capacity, cost, rev; edge() {} edge(int to, int _capacity, int _cost, int _rev) :to(to), capacity(_capacity), cost(_cost), rev(_rev) {} }; struct Min_Cost_Max_Flow { int V, H[N + 5], dis[N + 5], PreV[N + 5], PreE[N + 5]; vector<edge> G[N + 5]; //调用前初始化 void Init(int n) { V = n; for (int i = 0; i <= V; ++i)G[i].clear(); } //加边 void add(int from, int to, int cap, int cost) { G[from].push_back(edge(to, cap, cost, G[to].size())); G[to].push_back(edge(from, 0, -cost, G[from].size() - 1)); } //flow是自己传进去的变量,就是最后的最大流,返回的是最小费用 pii Min_cost_max_flow(int s, int t, int f) { int flow = 0; int res = 0; fill(H, H + 1 + V, 0); while (f) {//f - 可以达到的最大的最大流 priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q; fill(dis, dis + 1 + V, INF); dis[s] = 0; q.push(pair<int, int>(0, s)); while (!q.empty()) { pair<int, int> now = q.top(); q.pop(); int v = now.second; if (dis[v] < now.first)continue; for (int i = 0; i < G[v].size(); ++i) { edge& e = G[v][i]; if (e.capacity > 0 && dis[e.to] > dis[v] + e.cost + H[v] - H[e.to]) { dis[e.to] = dis[v] + e.cost + H[v] - H[e.to]; PreV[e.to] = v; PreE[e.to] = i; q.push(pair<int, int>(dis[e.to], e.to)); } } } if (dis[t] == INF)break; for (int i = 0; i <= V; ++i)H[i] += dis[i]; int d = f; for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].capacity); f -= d; flow += d; res += d*H[t]; for (int v = t; v != s; v = PreV[v]) { edge& e = G[PreV[v]][PreE[v]]; e.capacity -= d; G[v][e.rev].capacity += d; } } return pii{flow,res}; } }; Min_Cost_Max_Flow MCMF; /* i - st[i,n] ed [i + n,2 * n] s - 2 * n + 3,t - st - 2 * n + 1,ed - 2 * n + 2 */ int main() { int ca;scanf("%d",&ca); while(ca--) { int n,k;scanf("%d %d",&n,&k); MCMF.Init(2 * n + 2); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); int s = 0,t = 2 * n + 2; for(int i = 1;i <= n;++i) { MCMF.add(s,i,1,0); MCMF.add(i,i + n,1,-a[i]); MCMF.add(i + n,2 * n + 1,1,0); } MCMF.add(2 * n + 1,2 * n + 2,k,0); for(int i = 1;i <= n;++i) { for(int j = i + 1;j <= n;++j) { if(a[j] >= a[i]) MCMF.add(i + n,j,1,0); } } pii ans = MCMF.Min_cost_max_flow(s,t,INF); printf("%d\n",-ans.second); } // system("pause"); return 0; }
Distribution of books:
首先很直白的一个思路是二分答案,然后去贪心。
但是这里贪心思考了一会觉得都不是很满足最优的性质。
我们考虑dp去维护:dp[i]表示前i本书最多可以分配给几个人。
那么显然有$dp[i] = max{\sum_{i = 1}^{j - 1} dp[j] + 1} $满足sum(j + 1 ~ i) <= x。(x为二分的值)
我们考虑先求得一个前缀和pre[i]。那么对于后面那个就是满足pre[i] - pre[j] <= x。
转移可得pre[j] >= pre[i] - x。可以发现我们可以维护每个pre上面的最值。
再一次性查询>=pre[i] - x的即可。
那么显然可以线段树来维护,不过要先对前缀和进行离散化。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int a[N],n,k,len; LL pre[N],b[N];//dp[i] - 前i本书能分配的最多人数 struct Node{int L,r,x;}node[N << 2]; void Pushup(int idx) { node[idx].x = max(node[idx << 1].x,node[idx << 1 | 1].x); } void build(int L,int r,int idx) { node[idx].L = L,node[idx].r = r,node[idx].x = 0; if(L == r) { return ; } int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); } void update(int x,int idx,int val) { if(node[idx].L == node[idx].r) { node[idx].x = max(node[idx].x,val); return ; } int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= x) update(x,idx << 1,val); else update(x,idx << 1 | 1,val); Pushup(idx); } int query(int L,int r,int idx) { if(node[idx].L >= L && node[idx].r <= r) return node[idx].x; int mid = (node[idx].L + node[idx].r) >> 1,mx = 0; if(mid >= L) mx = max(mx,query(L,r,idx << 1)); if(mid < r) mx = max(mx,query(L,r,idx << 1 | 1)); return mx; } bool check(LL x) { int mx = 0; build(1,len,1); if(pre[1] <= x) { mx = 1; update(a[1],1,1); } for(int i = 2;i <= n;++i) { LL ma = pre[i] - x; int pos = lower_bound(b + 1,b + len + 1,ma) - b; if(pos == len + 1) { if(pre[i] <= x) { update(a[i],1,1); mx = max(mx,1); } continue; } int ans = query(pos,len,1); if(ans == 0) { if(pre[i] <= x) { update(a[i],1,1); mx = max(mx,1); } } else { update(a[i],1,ans + 1); mx = max(mx,ans + 1); } } return mx >= k; } int main() { int ca;scanf("%d",&ca); while(ca--) { scanf("%d %d",&n,&k); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); memset(pre,0,sizeof(pre)); int tot = 0; for(int i = 1;i <= n;++i) pre[i] = pre[i - 1] + a[i],b[++tot] = pre[i]; sort(b + 1,b + n + 1); len = unique(b + 1,b + n + 1) - b - 1; for(int i = 1;i <= n;++i) { a[i] = lower_bound(b + 1,b + len + 1,pre[i]) - b; } LL L = -INF,r = INF,ans; while(L <= r) { LL mid = (L + r) >> 1; if(check(mid)) ans = mid,r = mid - 1; else L = mid + 1; } printf("%lld\n",ans); } //system("pause"); return 0; }
Blow up the city:
这题其实和洛谷灾难那题差不多。
我们建立虚点对所有中央指挥点连边。
考虑最终形成的支配树,计算出每个点的深度就可以得出。
对于两个点u,v的答案就是dep[u] + dep[v] - dep[lca[u,v]] - 1.这里要把虚点的贡献减掉。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; vector<int> G[N],RG[N],T[N]; int dfn[N],rk[N],idom[N],semi[N],fa[N],out[N],dep[N],f[N][20],lg[N],n,m,tim = 0; namespace Node{ int fa[N],val[N]; void init() { for(int i = 1;i <= n + 1;++i) fa[i] = i,val[i] = 0; } int Find(int x) { if(x == fa[x]) return x; int ffa = fa[x]; fa[x] = Find(fa[x]); if(dfn[semi[val[x]]] > dfn[semi[val[ffa]]]) val[x] = val[ffa]; return ffa; } void Merge(int x,int y) { x = Find(x),y = Find(y); fa[y] = x; } }; void dfs(int u) { dfn[u] = ++tim; rk[tim] = u; for(auto v : G[u]) { if(!dfn[v]) { fa[v] = u; dfs(v); } } } void dfs2(int u,int ffa) { f[u][0] = ffa; dep[u] = dep[ffa] + 1; for(int i = 1;i <= lg[dep[u]];++i) f[u][i] = f[f[u][i - 1]][i - 1]; for(auto v : G[u]) { if(v == ffa) continue; dfs2(v,u); } } int LCA(int x,int y) { if(dep[x] < dep[y]) swap(x,y); while(dep[x] > dep[y]) x = f[x][lg[dep[x] - dep[y]] - 1]; if(x == y) return x; for(int i = lg[dep[x]];i >= 0;--i) { if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i]; } return f[x][0]; } int getmin(int x,int y) { return dfn[x] < dfn[y] ? x : y; } void solve() { dfs(n + 1); dfn[0] = n + 2; Node::init(); for(int i = n + 1;i >= 1;--i) { int x = rk[i]; for(auto v : RG[x]) { if(dfn[v] < dfn[x]) semi[x] = getmin(semi[x],v); else { Node::Find(v); semi[x] = getmin(semi[x],semi[Node::val[v]]); } } for(auto v : T[x]) { Node::Find(v); int ma = Node::val[v]; if(semi[ma] == x) idom[v] = x; else idom[v] = ma; } Node::val[x] = x; Node::Merge(fa[x],x); T[semi[x]].push_back(x); } for(int i = 2;i <= n + 1;++i) { int x = rk[i]; if(idom[x] != semi[x]) idom[x] = idom[idom[x]]; } for(int i = 1;i <= n + 1;++i) G[i].clear(); for(int i = 2;i <= n + 1;++i) { int x = rk[i]; if(idom[x]) { G[idom[x]].push_back(x); } } dfs2(n + 1,0); } void init() { for(int i = 1;i <= n + 1;++i) { dfn[i] = semi[i] = idom[i] = out[i] = 0; G[i].clear(); RG[i].clear(); T[i].clear(); } tim = 0; } int main() { for(int i = 1;i < N;++i) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); int ca;scanf("%d",&ca); while(ca--) { scanf("%d %d",&n,&m); init(); while(m--) { int u,v;scanf("%d %d",&u,&v); G[v].push_back(u); RG[u].push_back(v); out[u]++; } for(int i = 1;i <= n;++i) { if(out[i] == 0) { G[n + 1].push_back(i); RG[i].push_back(n + 1); } } solve(); int q;scanf("%d",&q); while(q--) { int a,b;scanf("%d %d",&a,&b); int ans = dep[a] + dep[b] - dep[LCA(a,b)] - 1; //printf("a is %d b is %d lca is %d\n",a,b,LCA(a,b)); printf("%d\n",ans); } } // system("pause"); return 0; } /* 2 9 9 1 2 3 4 3 5 4 6 4 7 5 7 6 8 7 8 7 9 10 1 2 1 3 4 7 4 5 9 5 3 8 6 4 */