2018 nowcoder 多校 5

场上差点4题……
补题进度[5/10] 场上3题

A gpa
变一下式子发现就是01分数规划&这下不会忘了、

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000007
const int maxn = 1e5 + 5;
const double eps = 1e-6;
double s[maxn],c[maxn];
int n,k;
double  a[maxn];
bool check(double mid)
{
    for(int i = 0;i<n;i++)
    {
       a[i] = s[i]*c[i] - s[i]*mid;
    }
    sort(a,a+n,[](double x,double y){return x > y;});
    double sum = 0;
    for(int i = 0;i < n - k;i++)
    {
        sum += a[i];
    }
    return sum >= 0;
}
int main ()
{

    cin>>n>>k;
    for(int i = 0;i<n;i++)
    {
        cin>>s[i];
    }
    for(int j = 0;j<n;j++)
    {
        cin>>c[j];
    }
    double l = 0.0,r = 1e12+5;
    while(r - l > eps)
    {
        double mid =(l + r) /2;
        if(check(mid))
        {
            l = mid;
        }
        else r = mid;
    }

    cout<<fixed<<setprecision(12)<<r<<endl;
    return 0;
}

E room
题意是搬宿舍,给出原来的住宿情况和之后的申请情况,问最少几个人交换就行了。
想了一下这样建图,原来的房间在左边,之后的申请看作是虚拟的房间,在右边,那么对于左边的点,连一条和对应的虚拟房间的交集(就是i中有多少个人到j这个宿舍) 然后就是二分图最大权匹配的问题了。
套了km算法的板子,其实费用流求最大费用也ok

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数
int g[N][N];//二分图描述
int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
int slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
    visx[x] = true;
    for(int y = 0; y < ny; y++)
    {
        if(visy[y])
            continue;
        int tmp = lx[x] + ly[y] - g[x][y];
        if(tmp == 0)
        {
            visy[y] = true;
            if(linker[y] == -1 || DFS(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if(slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}
int KM()
{
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i = 0; i < nx; i++)
    {
        lx[i] = -INF;
        for(int j = 0; j < ny; j++)
            if(g[i][j] > lx[i])
                lx[i] = g[i][j];
    }
    for(int x = 0; x < nx; x++)
    {
        for(int i = 0; i < ny; i++)
            slack[i] = INF;
        while(true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(DFS(x))
                break;
            int d = INF;
            for(int i = 0; i < ny; i++)
                if(!visy[i] && d > slack[i])
                    d = slack[i];
            for(int i = 0; i < nx; i++)
                if(visx[i])
                    lx[i] -= d;
            for(int i = 0; i < ny; i++)
            {
                if(visy[i])
                    ly[i] += d;
                else
                    slack[i] -= d;
            }
        }
    }
    int res = 0;
    for(int i = 0; i < ny; i++)
        if(linker[i] != -1)
            res += g[linker[i]][i];
    return res;
}
int x[N][5],y[N][5];
int main()
{
    int n;
    scanf("%d", &n);

    for(int i = 1; i<=n; i++)
    {
        for(int j = 1; j<=4; j++)
            scanf("%d",&x[i][j]);
    }
    for(int i = 1; i<=n; i++)
    {
        for(int j = 1; j<=4; j++)
            scanf("%d",&y[i][j]);
    }
    nx = ny = n;
    for(int i = 1; i<=n; i++)
    {
        for(int j = 1; j<=n; j++)
        {
            int c = 0;
            for(int k = 1; k<=4; k++)
            {
                for(int l = 1; l<=4; l++)
                {
                    if(x[i][k] == y[j][l])
                        c++;
                }
            }
            g[i-1][j-1] = c;
        }
    }
    int cost = KM();
    //int ans = minCostMaxflow(st,en,cost);
    //print();
    printf("%d\n",n*4-cost);
    return 0;
}

费用流版本:

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int from,to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//节点总个数,节点编号从0~N-1
void init(int n)
{
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
    edge[tol].to = v;
    edge[tol].from = u;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].from = v;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t)
{
    queue<int>q;
    for(int i = 0; i < N; i++)
    {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost )
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)
        return false;
    else
        return true;
}
//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s,int t,int &cost)
{
    int flow = 0;
    cost = 0;
    while(spfa(s,t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}

void print()
{
    for(int i = 0;i<tol;i += 2)
    {
        printf("%d -> %d flow = %d cap = %d cost = %d\n",edge[i].from,edge[i].to,edge[i].flow,edge[i].cap,edge[i].cost);
    }
}
int x[101][5],y[101][5];
int main()
{
    int n;
    scanf("%d", &n);
    init(2*n+2);
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=4;j++) scanf("%d",&x[i][j]);
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=4;j++) scanf("%d",&y[i][j]);
    }
    int st = 0,en = 2*n +1;
    for(int i = 1;i<=n;i++)
    {
        addedge(st,i,1,0);
        addedge(i+n,en,1,0);
        for(int j = 1;j<=n;j++)
        {
            int c = 0;
            for(int k = 1;k<=4;k++)
            {
                for(int l = 1;l<=4;l++)
                {
                    if(x[i][k] == y[j][l]) c++;
                }
            }
            addedge(i,j+n,1,4 - c);  //直接最小化需要搬出去的人数
        }
    }
    int cost = 0;
    int ans = minCostMaxflow(st,en,cost);
    //print();
    printf("%d\n",cost);
    return 0;
}

J plan
这道题就是一贪心,我的做法和std差不多,没想到判断了几个情况就过了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000007
int main ()
{
    int n,p2,p3;
    cin>>n>>p2>>p3;
    int t2 = (n + 2-1)/2;
    int t3 = (n + 3-1)/3;
    ll ans = min((ll)p2*t2,(ll)p3*t3);
    if(n % 2 == 1) ans = min(ans,(ll)(t2 - 2)*p2+p3);
    if(n % 3 == 1) ans = min(ans,(ll)(t3 - 2)*p3 + 2*p2);
    if(n % 3 == 2) ans = min(ans,(ll)(t3 - 1)*p3 + p2);
    cout<<ans<<endl;
    return 0;
}

G max
队友直接暴力了,其实只要取m = n/c m*(m-1) 判断一下特殊情况就ok

#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <set>
#include <map>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
int main()
{
    long long  n,c;
    cin>>c>>n;
    n/=c;
    for(long long  i=n+n;i>=2;i--){
        for(long long  j=int(i/2);j<=n&&j<i;j++){
                if(__gcd(j,i-j)==1) return cout<<c*c*j*(i-j)<<endl,0;
        }
    }
    cout<<-1<<endl;
    return 0;
}

F take
首先能想到一个点贡献的期望是前面都比他大的期望,首先按照价值对钻石排序,然后离线操作,每次添加之前先查询一下这个位置前面的拿不到的概率的前缀积,更新答案之后插入。这样就可以保证查询的时候,比当前大的已经插入了。

#include <bits/stdc++.h>
#define pr pair<ll,ll>
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn = 1<<17;
const ll mod = 998244353;
ll pow_mod(ll a,ll n)
{
    ll res = 1,b = a % mod;
    while(n)
    {
        if(n & 1) res = res * b % mod;
        b = b * b % mod;
        n>>=1;
    }
    return res;
}
ll bit[maxn];
int lowbit(int x)
{
    return x & (-x);
}
void add(int x,ll c)
{
    for(int i = x ; i< maxn; i += lowbit(i))
    {
        bit[i] = bit[i]*c% mod;
    }
}
ll prd(int x)
{
    ll res = 1;
    for(int i = x ;i ;i -= lowbit(i)) res = (res *bit[i])% mod;
    return res;
}
struct node
{
    int p,d,id;
}a[maxn];
int main()
{
    ll inv = pow_mod(100,mod-2);
    int n;
    scanf("%d",&n);
    for(int i = 0;i<maxn;i++) bit[i] = 1;
    for(int i = 0;i<n;i++)
    {
        a[i].id = i+1;
        scanf("%d%d", &a[i].p, &a[i].d);
    }
    sort(a,a+n,[](const node &x,const node &y)
         {
            if(x.d == y.d) return x.id < y.id;
            else return x.d > y.d;
         });
    ll ans = 0;
    for(int i = 0;i<n;i++)
    {
        ll tmp = (prd(a[i].id - 1)* ((a[i].p*inv) % mod))% mod;
       // cout<<tmp<<endl;
        ans = (ans + tmp) % mod;
        add(a[i].id,inv*(100 - a[i].p)% mod);
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值