2021牛客暑期多校训练营3补题

B.Black and white

题目思路

题目给出条件,(i,j),(i+1,j),(i,j+1),(i+1,j+1)中三个点被覆盖后第四个点也一定的覆盖是不需要花费的。
我们将横坐标和纵坐标看作端点来建边。
假设(i,j),(i+1,j),(i,j+1)被覆盖,那么有 A i < − > B j A_i<->B_j Ai<>Bj, A i + 1 < − > B j A_{i+1}<->B_j Ai+1<>Bj, A i < − > B j + 1 A_i<->B_{j+1} Ai<>Bj+1这三条双向边。
有着三条边存在我们可以推出来 A i + 1 A_{i+1} Ai+1 B j + 1 B_{j+1} Bj+1之间是存在路径的。根据题目的意思他们两个点之间联通是不需要花费的。
因为题目要求的是覆盖棋盘的最小花费,我们可以将这个问题转化成将图中各点相互连通的最小花费,我们用最小生成树算法解决。
这题卡了空间,所以数组要注意,经量少用longlong

ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 1e4+10;
const int inf = 1e9+10;
const ll llinf =1e18+10;
const ll mod = 1e9+7;

int n,m,a,b,c,d,p;

int e[maxn][maxn],dis[maxn*2];
bool book[maxn*2];


void init()
{
    for(int i=1; i<=n+m; i++)
        for(int j=1; j<=n+m; j++)
            if(i==j)e[i][j]=0;
            else
                e[i][j]=inf;
}

ll prim(int n)
{
    ll ans=0;
    memset(book,false,sizeof(book));
    book[1]=true;
    for(int i=1; i<=n; i++)
        dis[i]=e[1][i];
    for(int i=2; i<=n; i++)
    {
        ll minn=llinf,u=-1;
        for(int j=1; j<=n; j++)
        {
            if(!book[j]&&dis[j]<minn)
            {
                minn=dis[j];
                u=j;
            }
        }
        if(ans==inf)
            return -1;
        ans+=minn;
        book[u]=true;
        for(int v=1; v<=n; v++)
            if(!book[v])
                dis[v]=min(dis[v],e[u][v]);
    }
    return ans;
}

int main()
{

    scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
    init();
    ll tem=a;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int id=(i-1)*m+j;
            tem=(tem%p*tem%p*b%p+tem*c%p+d%p)%p;
            e[i][j+n]=e[j+n][i]=tem;
        }

    }
    n=n+m;
    cout<<prim(n)<<endl;

}

这题结论其实挺好想的,但是我估计我直接看这道题的话很难想到建边啥的。。。。

J.Counting Triangles

题目思路

因为我们要找的是三个点,数据也不支持 n 3 n^3 n3枚举,所以反向思考找异色三元组数量,最后从总数中减去,剩下的就是同色了。
因为只用两种颜色涂色,所以异色三元组会有两条边相同,剩下那条边异色。
根据这个性质,我们可以直接枚举起点,再枚举每个起点的边,记录颜色为1的和为0的,每次更新前计算他的贡献就好。

ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 3e5+10;
const int inf = 1e9+10;
const ll llinf =1e18+10;
const ll mod = 1e9+7;


namespace GenHelper
{
    unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
      	u = 0;
    }
}
using namespace GenHelper;

bool edge[8005][8005];
int n, seed;
vector<int>vec1[8005],vec0[8005];

int main() {

    cin >> n >> seed;
    srand(seed);
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            edge[j][i] = edge[i][j] = read();
        }
    }
    ll ans=1LL*n*(n-1)*(n-2)/6;
	ll res=0;
	for (int i=1; i<=n; i++){
		int black=0,white=0;
		for (int j=1; j<=n; j++){
			if (i==j) continue;
			if (edge[i][j]) 
            {
                res+=white; 
                black++;
            }
			else 
            {
                res+=black; 
                white++;
            }
		}
	}
	ans-=res/2;
	printf("%lld\n",ans);

    return 0;
}

F. 24dian

题目思路

搜索模拟题。我们先找到所有元素为n的集合,然后去暴力判断它是否满足情况。
我一开始的想法是枚举四个位置上的数,然后往中间差符号。
写到后面发现答案一直多一些结果,发现我这样枚举少了一些情况,例如:(a+b) * (c+d)、(a * b) + (c * d)等。
最后把这些情况枚举一了一遍就ac了。

ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9+10;
const ll llinf = 1e18+10;
const ll mod = 1e9+7;

int n;
double m;
vector<double>vec,an[13*13*13*13+100];
double a[maxn];
int cnt=0,flagx=0,flagy=0;
queue<double>q;

bool judge(double x,double y) {
	if (x>(int)x+1e-9) return 1;
	if (y>(int)y+1e-9) return 1;
	if (x/y>(int)(x/y)+1e-9) return 1;
	return 0;
}

void dfs1(int x,double ans,int flag)
{
    if(x==n)
    {
        //printf("%lf ",ans);
        if(ans<0)ans*=-1;
        if(fabs(ans-m)<=1e-9)
        {
            flagx++;
            if(flag)
                flagy++;
        }
        return;
    }
    dfs1(x+1,a[x]+ans,flag);
    dfs1(x+1,ans-a[x],flag);
    dfs1(x+1,ans*a[x],flag);
    int temflag=0;
    if(judge(ans,a[x]))
    {
        temflag=1;
    }
    dfs1(x+1,ans/a[x],temflag|flag);
    if(ans==0.0)return;
    temflag=0;
    if(judge(a[x],ans))
    {
        temflag=1;

    }
    dfs1(x+1,a[x]/ans,temflag|flag);
}

void check(double x,double y,int flag)
{
    if(y==-1.0)
    {
        if(x<0)x*=-1;
        if(fabs(x-m)<=1e-9)
        {
            flagx++;
            if(flag)
                flagy++;
        }
        return;
    }

    check(x+y,-1.0,flag);
    check(x-y,-1.0,flag);
    check(x*y,-1.0,flag);
    if(y!=0.0)
        check(x/y,-1,flag|judge(x,y));
    if(x!=0.0)
        check(y/x,-1,flag|judge(y,x));

}

bool ck()
{
    for(int i=0;i<vec.size();i++)
        a[i]=vec[i];
    flagx=0;
    flagy=0;
    do{
        dfs1(1,a[0],0);
        double x=a[0],y=a[1],c=a[2],d=a[3];
        check(x+y,c+d,0);
        check(x+y,c-d,0);
        check(x+y,c*d,0);
        check(x+y,c/d,judge(c,d));
        check(x+y,d/c,judge(d,c));

        check(x-y,c+d,0);
        check(x-y,c-d,0);
        check(x-y,c*d,0);
        check(x-y,c/d,judge(c,d));
        check(x-y,d/c,judge(d,c));

        check(x*y,c+d,0);
        check(x*y,c-d,0);
        check(x*y,c*d,0);
        check(x*y,c/d,judge(c,d));
        check(x*y,d/c,judge(d,c));

        check(x/y,c+d,judge(x,y));
        check(x/y,c-d,judge(x,y));
        check(x/y,c*d,judge(x,y));
        check(x/y,c/d,judge(x,y)|judge(c,d));
        check(x/y,d/c,judge(x,y)|judge(d,c));

        check(y/x,c+d,judge(y,x));
        check(y/x,c-d,judge(y,x));
        check(y/x,c*d,judge(y,x));
        check(y/x,c/d,judge(y,x)|judge(c,d));
        check(y/x,d/c,judge(y,x)|judge(d,c));

    }while(next_permutation(a,a+n));
    if(flagx>0&&flagy>0&&flagx==flagy)
    {

        return true;
    }else
    {
        return false;
    }
}

void dfs(int x,int pre)
{

    if(x>=n+1)
    {

        if(ck())
        {
            an[++cnt]=vec;
        }
        return;
    }
    for(double i=pre;i<=13;i++)
    {
        vec.push_back(i);
        dfs(x+1,i);
        vec.pop_back();
    }
}


int main()
{
    scanf("%d%lf",&n,&m);
    dfs(1,1);
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)
    {
        for(int j=0;j<an[i].size();j++)
        {
            printf("%.0lf ",an[i][j]);
        }
        printf("\n");
    }
}

C.Minimum grid

题目思路

根据题目意思,我们发现当行和列的最大值k相同时,我们就能少防止一个k值。
所以我们要求最小花费,就一定要最大化这种情况。
如果我们将行和列看作一个二分图两侧的点,那么上面说的这种情况很想二分图左右两个点匹配上。
所以我们考虑建二分图求解。
我们记录每个最大值k所对应的行和列。对于同一个k,我们将对应的行列建二分图,跑最大匹配,最后这个k的总贡献就是(行的个数+列的个数-最大匹配个数)*k。
我们枚举所有的k,最后的总值就是答案了
我用的是最大流求最大匹配

ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pl printf("\n");
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9+10;
const ll llinf = 1e18+10;
const ll mod = 1e9+7;

int n,m,k;
int mp[2000+10][2000+10];
int s,t,cnt;
int first[maxn];
int dep[maxn];
int cur[maxn];

struct node
{
    int to,next,w;
}e[maxn];

inline bool bfs()
{
    for(int i=s;i<=t;i++)dep[i]=-1;
    dep[s]=0;
    for(int i=s;i<=t;i++)cur[i]=first[i];
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i;i=e[i].next)
        {
            int to=e[i].to;
            ll vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline int dfs(int u=s,int flow=inf)
{
    if(u==t)
        return flow;
    ll last=flow;
    for(int i=cur[u];i&&last;i=e[i].next)
    {
        cur[u]=i;
        int to=e[i].to;
        ll vol=e[i].w;

        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));
            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }
    }
    return flow-last;
}

inline int dinic()
{
    int ans=0;
    while(bfs())
    {
        ans+=dfs();

    }

    return ans;
}

vector<int>vec[maxn],r,c;

void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

int solve()
{
    for(int i=s;i<=t;i++)
        first[i]=0,cur[i]=0;
    cnt=1;

    for(int i=0;i<r.size();i++)
    {
        add(s,r[i],1);
        add(r[i],s,0);
    }
    for(int i=0;i<c.size();i++)
    {
        add(c[i]+n,t,1);
        add(t,c[i]+n,0);
    }
    for(int i=0;i<r.size();i++)
    {
        for(int j=0;j<c.size();j++)
        {
            int u=r[i];
            int v=c[j];
            if(mp[u][v])
            {
                add(u,v+n,1);
                add(v+n,u,0);
            }
        }
    }

    return dinic();
}

int main()
{
    int x;
    scanf("%d%d%d",&n,&m,&k);
    s=0,t=2*n+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        vec[x].push_back(i);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        vec[x].push_back(i+n);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        mp[u][v]=1;//这个地方之前加上了mp[v][u]=1,一直wa。。
    }
    ll ans=0;
    for(int i=k;i>0;i--)
    {
        for(int j=0;j<vec[i].size();j++)
        {
            int id=vec[i][j];
            if(id<=n)
            {
                r.push_back(id);
            }else
            {
                c.push_back(id-n);
            }
        }
        ans+=(r.size()+c.size()-solve())*1LL*i;
        r.clear();
        c.clear();
    }
    printf("%lld\n",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值