2021牛客多校训练营3

B Black and white

这题直接将行列转换成点,而把点看成两个行列点的边,这样就转换成了当行列点在一个连通块的时候,再来个已经是连通块的点的时候,就不需要加上他的权值了,所以一共有n + m 个点,最后又要使得所有点是连通的,也就是如果再加一条边一定可以形成环,而这条环是我们不需要加的,所以直接就转换成了最小生成树,n + m - 1条边,prim直接求。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 5005, INF = 0X3F3F3F3F;

int f[N * N];

int n,m;
int g[N * 2][N * 2];
int st[N * 2];
int dist[N * 2];
int k[N * 2];

int prim(){
    memset(dist, INF, sizeof dist);
    int res = 0;
    dist[1] = 0;
    for(int i = 0; i < n + m; i++){
        int t = -1;
        for(int j = 1; j <= n + m; j++) if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
        st[t] = true;
        if(dist[t] != INF) res += dist[t];
        for(int j = 1; j <= n + m; j++) dist[j] = min(dist[j], g[t][j]);
    }
    return res;
}



int main()
{
    int a,b,c,d,p;
    scanf("%d %d %d %d %d %d %d",&n,&m,&a,&b,&c,&d,&p);
    f[0] = a;
    for(int i = 1;i <= n * m;i ++ ) f[i] = (1ll * f[i - 1] * f[i - 1] * b + f[i - 1] * c + d) % p;
    memset(g,INF,sizeof g);
    for(int i = 1; i <= n + m; i++) g[i][i] = 0;
    int cnt = 0;
    for(int i = 1;i <= n;i ++ )
    {
        for(int j = n + 1;j <= n + m;j ++ )
        {
            g[i][j] = f[m * (i - 1) + j - n];
            g[j][i] = f[m * (i - 1) + j - n];
        }
    }
    
    printf("%d\n",prim());
}

C Minimum grid

这题和上面一道题一样,也是将行列转换成点,由于我们只能在特定位置填数,所以只能在对应的行列加上边,然后从大到小枚举最大值,我们可以发现,行点是不会有边的,列点之间也不会有边,这不就是典型的二分图吗》》》。所以我们每次求一下当前二分图的最大匹配,假如我们对于当前的行列都填上这个最大值,显然不是最优的,但是如果当前行和当前列有相交的点可以填数,我当然把数填到这里,这样只用了一个数就满足了当前行列的最大值,这就是一个匹配数,所以我们求出最大匹配数,再用行列之和减去最大匹配数,就是当前最最大值的贡献了。

#include<bits/stdc++.h>

using namespace std;

const int N = 2010, M = 4010, A = 1e6 + 10;

int a[N],b[N];
int h[M],e[M],ne[M],idx;
int g[N][N];
vector<int> row[A],col[A];
bool st[M];
int match[M];
int n,m,k;


void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}

bool dfs(int u)
{
    for(int i = h[u];i != -1;i = ne[i])
    {
        int j = e[i];
        if(!st[j])
        {
            st[j] = true;
            if(match[j] == 0 || (dfs(match[j])))
            {
                match[j] = u;
                return true;
            }
        }
    }
    return false;
}

int solve()
{
    memset(match,0,sizeof match);
    int res = 0;
    for(int i = 1;i <= n;i ++ )
    {
        memset(st,false,sizeof st);
        if(dfs(i)) res ++;
    }
    return res;
}

int main()
{
    scanf("%d %d %d",&n,&m,&k);
    for(int i = 1;i <= n;i ++ )
    {
        scanf("%d",&a[i]);
        row[a[i]].push_back(i);
    }
    for(int i = 1;i <= n;i ++ )
    {
        scanf("%d",&b[i]);
        col[b[i]].push_back(i + n);
    }
    while(m -- )
    {
        int a,b;
        cin >> a >> b;
        g[a][b] = 1;
    }
    int ans = 0;
    for(int x = k;x >= 0;x -- )
    {
        if(row[x].size() == 0 && col[x].size() == 0) continue;
        //建图
        memset(h,-1,sizeof h);
        idx = 0;
        for(auto i : row[x])
        {
            for(auto j : col[x])
            {
                if(g[i][j - n] != 0) add(i,j), add(j,i);
            }
        }
        int res = solve();
        //cout << res << endl;
        ans += (col[x].size() + row[x].size() - res) * x;
    }
    printf("%d\n",ans);
    return 0;
}

E Math

这题公式推导就不写了,虽然赛前不会,但是赛后感觉就挺简单的。后来队友又做了一道题,他说好像也用到了这种方法,感觉这个方法应对这种题还是不错,不过我觉得打表也很好,直接找规律,毕竟赛时紧张,可能不会去推。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6;
typedef double ll;
const ll INF = 1e18;

ll a[7000007];
int tot = 0;

void init(){
	for(int i=2;i<=N;i++){
		ll x=1ll*i*i*i;
		a[++tot]=x;
		ll t=x*i*i-i;
		if(t>INF)continue;
		ll prea=x;
		while(t<INF){
			a[++tot]=t;
			t=1ll*t*i*i-prea;
			prea=a[tot];
		}
	}
	sort(a+1,a+1+tot);
}


int main()
{
    init();
    int t;
    cin >> t;
    while(t -- )
    {
        ll n;
        scanf("%lf",&n);
        int pos = lower_bound(a + 1,a + 1 + tot,n) - a;//
        if(a[pos] > n) pos --;
        printf("%d\n",pos + 1);
    }
}

F 24dian

这题暴力枚举,深搜。不过我dfs还不太会,感觉有些时候想不通,应该是人脑的栈不够用吧。。。。直接把所有情况列举出来,判断当前经过±*/后是否可以等于给定值,但是注意运算过程中要出现分数,我之前还以为到最终结果之前每一步都要出现分数,结果看了下代码,发现对于这种方式,只需要出现过就行了。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6;
const double eps = 1e-5;

int n,m;

struct node
{
    int a[4];
};

vector<node> ans;
double a[4];
bool frac, is;
bool vis[N];


void dfs(int cnt,bool flag)
{
    if(cnt == n)
    {
        if(fabs(a[0] - m) < eps)
        {
            is = true;
            if(!flag) frac = true;
            return ;
        }
    }
    // 判断是否出现了分数只要一出现,frac = true
    for(int i = 0;i < n;i ++ ) if(fabs(floor(a[i]) - a[i]) > eps) flag = true;
    for(int i = 0;i < n;i ++ )
    {
        for(int j = i + 1;j < n;j ++ )
        {
            if(vis[i] || vis[j]) continue;
            vis[j] = true;
            double x = a[i], y = a[j];
            a[i] = x + y;
            dfs(cnt + 1,flag);
            a[i] = x - y;
            dfs(cnt + 1,flag);
            a[i] = y - x;
            dfs(cnt + 1,flag);
            a[i] = x * y;
            dfs(cnt + 1,flag);
            if(fabs(y) > eps)
            {
                a[i] = x / y;
                dfs(cnt + 1,flag);
            }
            if(fabs(x) > eps)
            {
                a[i] = y / x;
                dfs(cnt + 1,flag);
            }
            vis[j] = false;
            a[i] = x, a[j] = y;
        }
    }
}


void DFS(int cnt,int x)
{
    if(cnt == n)
    {
        frac = false, is = false;
        dfs(1,false);
        if(!frac && is)
        {
            ans.push_back((node){(int)a[0],(int)a[1],(int)a[2],(int)a[3]});
        }
        return ;
    }
    for(int i = x;i <= 13;i ++ )
    {
        a[cnt] = i;
        DFS(cnt + 1,i);
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    DFS(0,1);
    cout << ans.size() << endl;
    for(int i = 0;i < ans.size();i ++ )
    {
        for(int j = 0;j < n;j ++ )
            cout << ans[i].a[j] << " \n"[j == n - 1];
    }
    return 0;
}```

J Counting Triangles

这题虽然是签到,但是我觉得真的是好题呀,一看一大片人不会,赛后一看题解,立马会了。。。题解已经很清楚了。再附上一道acwing额的题https://www.acwing.com/problem/content/3781/

#include<bits/stdc++.h>

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;
using namespace std;

typedef long long ll;

const int N = 8010;

bool edge[8005][8005];
ll white[N], black[N];

ll res = 0;

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值