2021牛客暑期多校训练营3

J题 Counting Triangles

题目描述

Goodeat finds an undirected complete graph with n vertices. Each edge of the graph is painted black or white. He wants you to help him find the number of triangles (a, b, c) (a < b < c), such that the edges between (a, b), (b, c), (c, a) have the same color. To avoid the input scale being too large, we use the following code to generate edges in the graph.

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 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();
 	return 0;
}

The edge array in the above code stores the color of the edges in the graph. e d g e [ i ] [ j ] = 1 edge[i][j]=1 edge[i][j]=1 means that the edge from i i i to j j j is black, otherwise it is white ( ∀ 0 ≤ i ≠ j ≤ n − 1 ( \forall 0 \le i \neq j \le n-1 (0i=jn1).

Ensure that there is an approach that does not depend on the way the data is generated.

输入描述:

The first line contains two integers n ( n ≤ 8000 ) n(n \le 8000) n(n8000), seed ( s e e d ≤ 1 0 9 ) (seed \le 10^9) (seed109), denote the number of vertices and the seed of random generator.

输出描述:

Output a line denoting the answer.

示例1

输入

10 114514

输出

35

说明

There’re 35 35 35 triangles that all three edges have the same color.

思路

合法的三角形需要枚举三个点,不合法的三角形只需要知道一个点相连的黑边与白边的数量。

#include<bits/stdc++.h>
using namespace std;

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;
int black[8005],write[8005];
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++)
            if(read())  black[i]++,black[j]++;
            else    write[i]++,write[j]++;
    long long ans=0;
    for(int i=0;i<n;i++)
        ans+=1LL*black[i]*write[i];
    cout<<1LL*n*(n-1)*(n-2)/6-ans/2<<endl;
}

B题 Black and white

题目描述

Goodeat has a white chessboard with n rows and m columns.

Each grid (i, j) has a weight c(i, j). At any time, the grid (i, j) can be dyed black at the cost of c(i, j).

Goodeat has a special talent. For the four intersecting squares of any two rows and two columns, if three of them are black squares, Goodeat can dye the fourth square black without any cost.

Please find out the minimum cost of dyeing a black chessboard.

Due to the large number of grids, we use the following method to generate weights:

A 0 = a A_0 = a A0=a
A ( i + 1 ) = ( A i ∗ A i ∗ b + A i ∗ c + d )   %   p A(i+1) = (A_i * A_i * b + A_i * c + d)\ \%\ p A(i+1)=(AiAib+Aic+d) % p

Where A ( m ∗ ( i − 1 ) + j ) A(m*(i-1)+j) A(m(i1)+j) is the cost c ( i , j ) c(i, j) c(i,j) of the grid in the i-th row and j-th column ( 1 ≤ i ≤ n , 1 ≤ j ≤ m ) (1 \le i \le n, 1\le j\le m) (1in,1jm).

输入描述:

The first line contains seven integers n , m , a , b , c , d , p n,m,a,b,c,d,p n,m,a,b,c,d,p. ( a , b , c , d < p ≤ 100000 , n , m ≤ 5000 (a,b,c,d <p\le 100000, n,m\le 5000 a,b,c,d<p100000,n,m5000

输出描述:

Output a single integer denoting the answer.

示例1

输入

4 4 1 2 3 4 7

输出

20

说明

Here is the weight matrix:
2 4 6 3
3 3 3 3
3 3 3 3
3 3 3 3

思路

把棋盘按照行列看作二分图,一共有 n + m n+m n+m 个结点。把 ( i , j ) (i,j) ij位置涂黑,则在两个结点直接连接一条边。最终目的是构建一个 “完全二分图”。

题目规定,若 ( a , b ) (a,b) (a,b) ( b , c ) (b,c) (b,c) ( c , d ) (c,d) (c,d) 都为黑色,则 ( a , d ) (a,d) (a,d) 也为黑色,说明这个连接具有传递性。则问题可以转换为求最小生成树。

Prim做法

稳定的 O ( n 2 ) O(n^2) O(n2) 做法。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e6,N=5010;
int n,m,a,b,c,d,p,ans,dis[N<<1],vis[N<<1],g[N][N];

void prim(){
	memset(dis,0x3f,sizeof(dis)); dis[0]=0;
	for(int i=0;i<n+m;i++){
		int x=-1;
		for(int j=0;j<n+m;j++)  if(!vis[j]&&(dis[j]<dis[x]||x==-1)) x=j;
		vis[x]=1;	ans+=dis[x];
		if(x<n) for(int j=0;j<m;j++) dis[n+j]=min(dis[n+j],g[x][j]);
		else	for(int j=0;j<n;j++) dis[j]=min(dis[j],g[j][x-n]);	
	}
}

signed main(){
	scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
	for(int k=0;k<n*m;k++){
		int i=k/m,j=k%m;
		g[i][j]=(1LL*a*a*b+1LL*a*c+d)%p;
		a=g[i][j];
	}
	prim();
	printf("%d\n",ans);
}

Kruskal做法

Kruskal 不适合稠密图,sort 排一次序直接超时。由于 P P P 的范围为 1 0 5 10^5 105,可以直接按值映射。

并查集如果只用压缩路径,时间复杂度是 O ( n 2 l o g n 2 ) O(n^2logn^2) O(n2logn2),还是会超时。用压缩路径+按秩合并,每次查询复杂度是一个反阿克曼函数,可以认为,五次操作之内,必求出根节点,可以通过。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e6,N=5010;
int n,m,a,b,c,d,p,ans,g[N*N],fa[N<<1];
vector<tuple<int,int>> vec[100010];

int get(int x){
	if(fa[x]<0) return x;
	return get(fa[x]);
}

void merge(int x,int y){
	if(fa[x]<fa[y]) fa[x]+=fa[y],fa[y]=x;
	else	fa[y]+=fa[x],fa[x]=y;
}

signed main(){
	scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
	for(int k=0;k<n*m;k++){
		int i=k/m,j=k%m+n;
		g[k]=(1LL*a*a*b+1LL*a*c+d)%p;
		vec[g[k]].push_back({i,j});a=g[k];
	}
	memset(fa,-1,sizeof(fa));
	for(int k=0;k<100010;k++){
		while(vec[k].size()){
			auto [i,j]=vec[k].back();
			vec[k].pop_back();
			int x=get(i),y=get(j);
			if(x==y)	continue;
			ans+=k;
			merge(x,y);
		}
	}
	printf("%d\n",ans);
}

E题 Math

题目描述

Given n n n, count the number of pairs of positive integers ( x , y ) (x, y) (x,y), such that x y + 1 ∣ x 2 + y 2 , 1 ≤ x ≤ y ≤ n xy+1|x^2+y^2, 1\le x\le y \le n xy+1x2+y2,1xyn.

There’re t test cases in total.

输入描述:

First line contains an integer t ( 1 ≤ t ≤ 1 0 5 ) t (1\le t\le 10^5) t(1t105). Next t lines each contains one integer n ( 1 ≤ n ≤ 1 0 18 ) n (1\le n \le 10^{18}) n(1n1018).

输出描述:

Output t t t lines, denoting the answer of each testcase.

示例1

输入

10

10

100

1000

10000

100000

114514

1919810

20190104

123123123123

10000001000000

输出

2
5
14
31
65
67
158
326
5226
22091

思路

x y + 1 ∣ x 2 + y 2 xy+1|x^2+y^2 xy+1x2+y2,可以转化为 x 2 − k x y + y 2 − k = 0 x^2-kxy+y^2-k=0 x2kxy+y2k=0 x x x 看作变量, y y y 看作常量。若我们已知一对 { x , y } \{x,y\} {x,y},那么由韦达定理,可得另一个 x ′ x' x x ′ = k y − x x'=ky-x x=kyx。再把 x x x 看作常量, y y y 看作变量,又可以退出一个 y ′ y' y,这样循环下来,可以知道第一组为 { 0 , k } \{0,\sqrt k \} {0k },以及可以退推每一组答案。先预处理出所有的答案,询问时二分查找。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,N=3e6;
int cnt,T,a[N],n;

signed main(){
	for(int i=2;i<=(int)1e6;i++){
		int k=i*i,x=0,y=i,t;
		while(true){
			if(k>(inf+x)/y)	break;
			t=y,y=y*k-x,x=t;
			a[++cnt]=y;
		}
	}
	sort(a+1,a+cnt+1);
	for(cin>>T;T;T--){
		cin>>n;
		cout<<upper_bound(a+1,a+1+cnt,n)-a<<endl;
	}
}

F题 24dian

题目描述

24dian is a famous game. You are given a set of n cards, each card contains an integer between 1 to 13. You can put the cards in arbitrary order, add + or - or * or / between any two cards, and add as many parentheses as you want. Your goal is to make the expression valid and its value equals m.

However, there’re cases when valid solutions exist, but all the valid solutions involve fractions, that is, non-integers, in the process of calculation. For example, if you want to use (1, 5, 5, 5) to get 24, the only solution is 5*(1-(1/5)), where 1/5 is a fraction.

Given the number of cards n and the result m you want to get, find all the possible set of cards that has a valid solution but any solution involves fraction in the calculation.

输入描述:

The first line contains two integers n , m ( 1 ≤ n ≤ 4 , 1 ≤ m ≤ 1 0 9 ) n,m(1\leq n\leq 4,1\leq m\leq 10^9) n,m(1n4,1m109) , representing the number of cards and the result you want to get.

输出描述:

First line contains an integer k k k, denoting the number of possible sets.
In next k k k lines, each line contains n n n non-decreasing numbers, represent a possible set. You should print the sets in lexicographical order.

示例1
输入

4 24

输出

16
1 3 4 6 
1 4 5 6 
1 5 5 5 
1 6 6 8 
1 8 12 12 
2 2 11 11 
2 2 13 13 
2 3 5 12 
2 4 10 10 
2 5 5 10 
2 7 7 10 
3 3 7 7 
3 3 8 8 
4 4 7 7 
5 5 7 11 
5 7 7 11

示例2

输入

1 114514

输出

0

思路

这题题意为:如果一组牌,只有出现分数,才能凑成 m m m 点,才算一组解。

写一个递归函数,每一步枚举当前操作的两个数字。函数还有一个返回值,返回1代表,不需要分数也可以凑成m点;返回值为2代表,只有出现分数,才能凑成m点;返回值为3,代表不可以凑成m点。显然,只有返回值为2才是满足要求的。

#include<bits/stdc++.h>
using namespace std;
vector<double> a; vector<vector<double>> ans;
int n,m;

bool equal(double x){ return (abs(x-m)<1e-6)?true:false; }

int solve(vector<double> a,bool flag){
	if(a.size()==1&&equal(a[0])){ return flag+1; }
	if(a.size()==1){ return 3; }
	int mn=3;
	for(int i=0;i<(int)a.size()&&mn!=1;i++){
		for(int j=i+1;j<(int)a.size()&&mn!=1;j++){
			vector<double> t;
			for(int k=0;k<(int)a.size();k++) if(k!=i&&k!=j) t.push_back(a[k]);
			t.push_back(a[i]+a[j]); mn=min(mn,solve(t,flag)); t.pop_back();
			t.push_back(a[i]-a[j]); mn=min(mn,solve(t,flag)); t.pop_back();
			t.push_back(a[j]-a[i]); mn=min(mn,solve(t,flag)); t.pop_back();
			t.push_back(a[i]*a[j]); mn=min(mn,solve(t,flag)); t.pop_back();
			if(a[j]){ t.push_back(a[i]/a[j]); mn=min(mn,solve(t,flag||(int)a[i]%(int)a[j]!=0)); t.pop_back(); }
			if(a[i]){ t.push_back(a[j]/a[i]); mn=min(mn,solve(t,flag||(int)a[j]%(int)a[i]!=0)); t.pop_back(); }
		}
	}
	return mn;
}

void choose(int beg){
	if((int)a.size()==n){ if(solve(a,0)==2)	ans.push_back(a);	return; }
	for(int i=beg;i<=13;i++){ a.push_back(i); choose(i); a.pop_back(); }
}

int main(){
	cin>>n>>m; choose(1);
	cout<<ans.size()<<endl;
	for(int i=0;i<(int)ans.size();i++)
		for(int j=0;j<n;j++)	cout<<ans[i][j]<<" \n"[j==n-1];
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51864047

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值