牛客第三场(磨练心态)

Minimum grid

请添加图片描述
题意
给出了一个n*n的矩阵,和矩阵中的一些位置,要求我们再这些位置中填数使得满足每一行的最大值满足b[i],每一列的最大值满足c[i];要求填的数之和最小。
请添加图片描述
对于这个问题,我们最大的数就是将每一行每一列的数都加起来,由于要求最小,所以我们要尽可能找到一个点,使它所在的每一行每一列均满足最大,由于一个点的值只有一个,所以它的行和列的最大值应该相同。这样,我们就可以从行和列的最大值入手,找出有多少个这样的点,我们每找出这样一个点,就相当与减少了这个最大值在最后结果中的贡献,这样,我们将这个最大值的行和列提取出来分别放在两个点集中,如果对应的这些点可以连成一条边(也就是在矩阵中a[i][j]是可填数的)每连上这样一条边,就可以减少这个最大值在矩阵中的贡献,由于最小,所以我们所求的就是这个行列的最大匹配(二部图每连一条线,我们所填的最大值就可以减少一个);对于每一个最大值,都有以它为最大值的【(行数+列数-二部图边数)*最大值】这样的贡献,最后对于每一个最大值都跑一个二部图的匹配,将所有最大值的贡献相加就是答案了(最大值从小到大,从大到小遍历都是可以的)

#include<vector>
#include<iostream>
#include<memory.h>
using namespace std;
#define int long long
const int M = 1000010;
const int N = 2010;
int mp[N][N];
vector<int>b[M],c[M];
int link[N];
int vis[N];
vector<int>to[N];
int dfs(int x)
{
    for(auto y : to[x])
    {
        if(vis[y]) continue;
        vis[y] = 1;
        if(!link[y] || dfs(link[y]))
        {
            link[y] = x;
            return 1;
        }
    }
    return 0;
}
int x,y;
signed main(){
    int ans=0;
    int n,m,k;
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        cin>>x;
        b[x].push_back(i);
    }
    for(int i=1;i<=n;i++){
        cin>>x;
        c[x].push_back(i);
    }
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        mp[x][y]=1;
    }
    for(int i=1;i<=k;i++){
        if(b[i].size()==0&&c[i].size()==0)continue;
        for(int j=1;j<=n;j++)to[j].clear();
        for(auto j:b[i]){
            for(auto z:c[i]){
                if(mp[j][z]==1)to[j].push_back(z);
            }
        }
        int res = 0;
        for(int j = 1;j <= n;++j)
        {
            memset(vis,0,sizeof vis);
            res += dfs(j);
        }
        ans += (b[i].size() + c[i].size() - res) * i;
    }
    cout<<ans<<endl;
}

Counting Triangles

(太长了不想贴图了)
题意
按照题目要求给了一个二维矩阵,数出这里面所有的三条边全为同样颜色的三角形个数
由于数出这样的三角形的个数比较难,所以我们可以换成另一种想法,用这些点所能形成的三角形个数减去含有异边的三角形个数
对于每个含异边的三角形,我们可以(惊讶的)发现,这些三角形一定是有两条同边一条异边的,也就是说,它一定是有两个异角,一个同角的,所以含有异边的三角型的个数一定是 异角的个数/2 用可以形成的三角形的个数减去含异角的三角形的个数,就是答案;

#include<iostream>
#include<vector>
#include<algorithm>
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];
long long w[8005];
long long bl[8005];
using namespace std;
int main() {
    long long n;
    int  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();
    long long ans = n * (n - 1) * (n - 2) / 6;//一共可以形成多少个三角形;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if(i==j)continue;
            if (edge[i][j] == 1)w[i]++;
            else bl[i]++;
        }
    }//统计不同角的个数;
    long long sum = 0;
    for (int i = 0; i < n; i++)sum += bl[i] * w[i];//异色角的数量;
    ans = ans - sum / 2;
    cout << ans << endl;
    return 0;
}

Math

请添加图片描述
请添加图片描述
就这样枚举上去由于数据范围是1e18,我们就需要选择离线算出,然后排序,最后二分输出答案(注意这里最后会有1e18*1e18,long long 是存不住的需要用__int128;

#include<bits/stdc++.h>
using namespace std;
const long long maxn = 1e7 + 10;
vector<long long>a(maxn);
int sum=0;
void init(){
	a[sum] = 1;
	sum++;
	for (long long i = 2; i <= 1000000; i++) {
		__int128 x = i * i * i;
		__int128 k = i;
		while (x <= 1000000000000000000) {
			a[sum] = x;
			sum++;
			__int128  p = x;
			x = x * i * i - k;
			k = p;
		}
	}
	sort(a.begin(), a.begin()+sum);
}
int main() {
	init();
	int t;
	cin >> t;
	while (t--) {
		long long n;
		cin >> n;
		int ans = lower_bound(a.begin(), a.begin()+sum, n) - a.begin();
		if (a[ans] == n)cout << ans + 1 << endl;
		else cout << ans << endl;
	}
}

Black and white

请添加图片描述
题意
有一个二维nm的cost矩阵,里面的cost满足cost[i][j]满足A(m(i-1)+j);其中A(0)=a,A(i+1)=(Ai * Ai * b + Ai * c + d)% p,其中我们每将三个黑色的方块涂黑,剩下的一个方块会自动涂黑,所以我们最少可以只在每一行每一列选择一个格子涂上黑色,通过这个选法生成一个最小生成树,这个最小生成树的权值和就是我们需要消耗的代价;然后跑一遍Prim或者是Kruskal求出权值和就行了

#include <bits/stdc++.h>
using namespace std;
const int N=5010;
const int M=3e7+10;
int A[M],fa[2*N];
struct node{
	int x,y;
	long long w;
	 bool operator < (const node & u) const { return w < u.w; }
}p[M]; 
const int INF=0x3f3f3f3f;
int n, m, a, b, c, d, e;
int find(int x){
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}
int js(long long x) {
	return (x * x * b + x * c + d) % e;
}
void jd(){
	A[0]=a;
	int flag=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			flag=m*(i-1)+j;
			A[flag]=js(A[flag-1]);
			p[flag]={i,j,A[flag]};
		}
	}
}
void solve(){
	cin>>n>>m>>a>>b>>c>>d>>e;
	jd();
	long long res=0,cnt=m+n-1;
	for(int i=1;i<=n+m;i++)fa[i]=i;
	sort(p+1,p+m*n+1);
	for(int i=1;i<=m*n;i++){
		int x=p[i].x;
		int y=p[i].y;
	    long long w=p[i].w;
		x=find(x);
		y=find(n+y);
		if(!cnt)break;
		if(x!=y){
			fa[x]=y;
			cnt--;
			res+=w;
		}
	}
	cout<<res<<endl;
}
int main(){
	ios::sync_with_stdio(0); 
	cin.tie(0); 
	cout.tie(0); 
	cin.exceptions(ios::badbit | ios::failbit);
	solve();
}

需要注意的是,内嵌的小于号重载会比cmp函数快上不少

24dian

请添加图片描述
题意
题目的意思是给我没1~13的数字,这些数字可以出现多次,要求我们将这些数字进行四则运算,同一种运算可以出现多次,但必须包含小数也就是除法,问我们到达结果 k 有多少种途径,这些途径是什么;用dfs模拟就好了,需要用到两个dfs,一个遍历所有的值,一个遍历四则运算,在结果处判断是否有除法出现以及是否等于结果,将满足情况的统一到一个数组中,输出它的长度以及它的值就好了。
还有就是要注意,可能会有除零这种情况发生,在除法的时候要判断一下。

#include <bits/stdc++.h>
using namespace std;
const int N=5010;
const int M=3e7+10;
const double mix=1e-12;
const int INF=0x3f3f3f3f;
vector<vector<int>>ans;
vector<int>zj;
bool v[10],flag=0,yy;
double a[5];
int n,k;
bool c(double x){
	return x-(int)x*1.0<mix;
}
//判断是否为整数
bool cmp(double x) {
    return fabs(x) < mix;
}
//判断大小
void dfss(int p,bool d){
	if(p==n){
		if(fabs(a[0]-k)<mix){
			if(d==1)yy=1;
			else flag=0;
		}
		return;
	}//四则运算后是否等于k
	for(int i=0;i<n;i++)if(c(a[i])==0)d=1;//是否是分数
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++){
			if(v[i]||v[j])continue;
			double x=a[i],y=a[j];
			v[j]=1;
			a[i]=x+y;
			dfss(p+1,d);
			a[i]=x*y;
			dfss(p+1,d);
			a[i]=fabs(x-y);
			dfss(p+1,d);
			if (!cmp(x))a[i] = y / x, dfss(p + 1, d);
            if (!cmp(y))a[i] = x / y, dfss(p + 1, d);
			v[j]=0;
			a[i]=x,a[j]=y;
		}
	}//遍历四则运算
}

void dfs(int s,int x){
	if (s == n) {
        flag=1;
	    yy=0;
	    memset(v,0,sizeof(v));
	    dfss(1,0);
	    if(flag==1&&yy==1){
		    zj.clear();
		    for(int i=0;i<n;i++)zj.push_back(a[i]);
		ans.push_back(zj);
	}
        return;
    }
    for (int i = x; i < 14; i++) {
        a[s] = i;
        dfs(s + 1, i);
    }
}//主dfs将所有值所对应的情况遍历,s记录多少层,x记录对应的值;
void solve(){
	cin >> n >> k;
    if (n < 4) {
        cout << 0 << endl;
        return;
    }//n小于四时是没有答案的,特判;
    dfs(0, 1);
    cout << ans.size() << endl;
    for(int i=0;i<ans.size();i++){
    	for(int j=0;j<ans[i].size();j++){
    		cout<<ans[i][j]<<" ";
		}
		cout<<endl;
	}
}
int main(){
	ios::sync_with_stdio(0); 
	cin.tie(0); 
	cout.tie(0); 
	cin.exceptions(ios::badbit | ios::failbit);
	solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值