《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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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

*/
View Code

 

posted @ 2021-08-15 07:41  levill  阅读(25)  评论(0编辑  收藏  举报