第五场
C题——(树的直径+dfs)
求一个树的非严格第二直径,我们首先可以直接求出树的直径,并记录下直径的两个端点,然后我们考虑分别以这两个端点(注意是两个,只对一个端点判断会漏掉一些情况)进行bfs,记录最大深度以及最大深度的个数,判断最大深度个数是否大于等于二,如果满足条件,答案就是最大深度,否则答案就是最大深度-1。
#define rp(i,s,t) for (int i = (s); i <= (t); i++) int vis[100007]; int dis[100007]; vector<int> G[100007]; int ans,pos; class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param e int整型vector 长度为n-1的数组,表示结点2到结点n的父结点 * @return int整型 */ void bfs(int s){ queue<int> q;q.push(s); vis[s]=1; while(!q.empty()){ int u=q.front();q.pop(); if(dis[u]>ans) ans=dis[u],pos=u; for(auto v:G[u]){ if(!vis[v]){ dis[v]=dis[u]+1; vis[v]=1; q.push(v); } } } } int tree3(vector<int>& v) { int n=v.size(); for(int i=0;i<n;i++){ G[i+2].push_back(v[i]); G[v[i]].push_back(i+2); } dis[1]=0; bfs(1); rp(i,1,n+1) vis[i]=0; rp(i,1,n+1) if(dis[i]>dis[pos]) pos=i; dis[pos]=0; bfs(pos); int Max=*max_element(dis+1,dis+2+n); int cnt=0; rp(i,1,n+1) if(dis[i]==Max) cnt++,pos=i; dis[pos]=0; rp(i,1,n+1) vis[i]=0; bfs(pos); rp(i,1,n+1) if(dis[i]==Max) cnt++; if(cnt>2) return Max; else return Max-1; } };
第七场
B题——巴什博弈变形
可以看出当p==q时,就是巴什博弈,然后考虑其他情况。
然后考虑p>q的情况,这时牛牛可以先取一定数量的贝壳,使得剩下的贝壳数是q+1的倍数。
当剩下的贝壳数是q+1的倍数时,这是牛妹的一个必败态,因为不论牛妹怎么取,牛牛都可以取一些贝壳,使得剩下的贝壳还是q+1的倍数,这样牛牛就是必胜的。
p<q的情况同理,不过因为是牛牛先取,需要考虑一种特殊情况,即当p>=n时,牛牛一次可以取完,牛牛必胜,否则牛牛必败。
class Solution { public: /** * * @param n int整型 * @param p int整型 * @param q int整型 * @return int整型 */ int Gameresults(int n, int p, int q) { if(p>=n||p>q) return 1; if(p==q){ if(n%(p+1)==0) return -1; else return 1; } return -1; } };
C题——(树的直径+dfs)
首先需要知道怎么求树的直径(dfs或树形dp),然后我们可以首先从1开始dfs出直径的一个端点,然后我们以这个端点为根,进行dfs,并用一个数组dp来记录当前节点的儿子节点的最大深度,并求出直径,最后再dfs一次,如果儿子节点的最大深度等于直径,则表明当前节点时直径上的点,对该点进行标记,同理以另一个端点为根进行同样的操作。
trick:注意不能只对一个端点进行标记,这样会漏掉一些情况,比如下面这个图。
建图数据:7,[1,2,2,1,5,5],[2,3,4,5,6,7]
正确答案:7
只对一个端点进行判断的答案是6。
#include <bits/stdc++.h> #define PI atan(1.0)*4 #define rp(i,s,t) for ( int i = (s); i <= (t); i++) #define RP(i,t,s) for ( int i = (t); i >= (s); i--) #define sc(x) scanf("%d",&x) #define scl(x) scanf("%lld",&x) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define pll pair<ll,ll> #define pil pair<int,ll> #define m_p make_pair #define p_b push_back #define ins insert #define era erase #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define dg if(debug) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"; using namespace std; int debug = 0; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll lcm(ll a,ll b){ return a/gcd(a,b)*b; } inline int read(){ int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; } const int N = 2e5+7; int u[N],v[N]; int vis[N]; vector<int> G[N]; int dp[N]; int pos; int Max; void dfs1(int u,int fa,int d){ if(d>Max){ pos=u; Max=d; } for(auto v:G[u]) if(v!=fa) dfs1(v,u,d+1); } void dfs2(int u,int fa,int d){ if(d>Max){ pos=u; Max=d; } dp[u]=d; for(auto v:G[u]){ if(v!=fa){ dfs2(v,u,d+1); dp[u]=max(dp[v],dp[u]); } } } class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n int整型 节点个数 * @param u int整型vector * @param v int整型vector * @return int整型 */ int PointsOnDiameter(int n, vector<int>& u, vector<int>& v) { rp(i,0,n-2){ G[u[i]].p_b(v[i]); G[v[i]].p_b(u[i]); } dfs1(1,0,0); int L=pos; Max=0; dfs2(L,0,0); rp(i,1,n) if(dp[i]==Max) vis[i]=1; int R=pos; dfs2(R,0,0); rp(i,1,n) if(dp[i]==Max) vis[i]=1; int cnt=0; rp(i,1,n) cnt+=vis[i]; return cnt; } };
第八场
C题——(floyd求最短乘积路)
一眼不难发现是到floyd最短路的题,但是发现维护的是乘法,直接进行维护的话精度不够(不能取余,取余会使得答案错误),因此需要转换成加法运算。
乘法怎么转换加法呢?可以通过取log实现。
那么解法就出来了,我们可以维护对边权取log后的加法最短路,同时用一个pair记录对应的实际乘法最短路的值。
而且边的权值定义为组合数,这里我们定义lg[i]为log(1)+log(2)+log(3)+.....+log(i),即log值的前缀和,对应阶乘取log后的值。
边权取log后就转换为。
最后我们套用floyd的模板就行了(不要忘记重载+号),答案就是G[s][t].second%mod。
#include<iostream> #define ll long long #include<algorithm> #include<vector> #define pdl pair<double,ll> using namespace std ; const ll mod = 1e9+7; pdl operator + (pdl a,pdl b){ return make_pair(a.first+b.first,a.second*b.second%mod); } pdl G[505][505]; int n,m; ll quickPow(ll a, ll b, ll mod) { ll ans = 1; while(b) { if(b&1) ans=(ans*a)%mod; a=(a*a)%mod; b>>=1; } return ans; } ll inv(ll x){ return quickPow(x,mod-2,mod)%mod; } ll fac[1007]; double lg[1007]; ll C(int n,int m){ return fac[n]*inv(fac[n-m])%mod*inv(fac[m])%mod; } void init(){ fac[0]=1; for(int i=1;i<=1000;i++) fac[i]=(fac[i-1]*i)%mod; for(int i=1;i<=1000;i++) lg[i]=(log(i)+lg[i-1]); } void floyd(){ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) G[i][j]=min(G[i][j],G[i][k]+G[k][j]); } class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n int整型 有n个城市 * @param m int整型 有m条路径 * @param s int整型 起始城市坐标 * @param t int整型 终点城市坐标 * @param edge int整型vector<vector<>> 边的格式如下:[[u1,v1,a1,b1],[u2,v2,a2,b2],...] * @return int整型 */ int minDist(int a,int b,int s, int t, vector<vector<int> >& edge) { n=a; m=b; init(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j) G[i][j]=make_pair(0,1ll); else G[i][j]=G[j][i]=make_pair(1e18,0); } } for(int i=0;i<m;i++){ vector<int> val=edge[i]; int x=val[0]; int y=val[1]; double w1=lg[val[2]]-lg[val[3]]-lg[val[2]-val[3]]; ll w2=C(val[2],val[3])%mod; G[x][y]=G[y][x]=min(G[x][y],make_pair(w1,w2)); } floyd(); return G[s][t].second%mod; } };
第十场
C题——并查集
把边权拆位后对每一位进行考虑,可以发现只有路径上的所有点的当前位都为1时才能对答案产生贡献,因此我们可以用并查集来维护连通块(保证连通块中的所有点的当前位为1,并且可以到达连通块中的其他点)。
假设当前位为i,当前连通块内的点数为num,对答案产生的贡献就是。
#include <bits/stdc++.h> #define PI atan(1.0)*4 #define rp(i,s,t) for (int i = (s); i <= (t); i++) #define RP(i,t,s) for ( int i = (t); i >= (s); i--) #define sc(x) scanf("%d",&x) #define scl(x) scanf("%lld",&x) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define pll pair<ll,ll> #define pil pair<int,ll> #define m_p make_pair #define p_b push_back #define ins insert #define era erase #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define dg if(debug) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"; using namespace std; int debug = 0; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll lcm(ll a,ll b){ return a/gcd(a,b)*b; } inline int read(){ int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; } const int N = 1e5+7; #define UnionFindSIZE 1234567 struct Union_Find { int d[UnionFindSIZE], num[UnionFindSIZE]; void init(int n){ for(int i = 0; i <= n; i++) { d[i]=i; num[i]=1; } } int find(int x){ int y = x, z = x; while(y != d[y]) { y = d[y]; } while(x != y) { x = d[x]; d[z] = y; z = x; } return y; } bool is_root(int x) { return d[x] == x; } bool uu(int x,int y) { x=find(x); y=find(y); if(x == y) { return 0; } if(num[x] > num[y]) { swap(x,y); } num[y] += num[x]; d[x] = y; return 1; } }U; int vis[N]; int u[N],v[N],w[N]; class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n int整型 点的个数 * @param u int整型vector 每条边的起点 * @param v int整型vector 每条边的终点 * @param p int整型vector 每个点的价值 * @return long长整型 */ long long solve(int n, vector<int>& uu, vector<int>& vv, vector<int>& ww) { ll ans=0; rp(i,0,20){ U.init(n); rp(j,0,n-1) w[j]=((ww[j]&(1<<i))!=0); rp(j,0,n-2) if(w[uu[j]]==1&&w[vv[j]]==1) U.uu(uu[j],vv[j]); rp(j,0,n-1){ if(!U.is_root(j)||!w[j]) continue; ans+=U.num[j]*(U.num[j]+1)/2*(1ll<<i); } } return ans; } };
第十一场
B题——卡特兰数+规律
手推几个样例或者暴力一下不难发现答案就是判断第i个卡特兰数的奇偶性,然后我们再找一下规律可以看出,当n-1==2^k时,输出true,否则为false。
因此我们可以直接用java的大数模拟一下就行了。
import java.util.*; import java.math.BigInteger; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n string字符串 三角形的长和高 * @return bool布尔型 */ public boolean judge (String n) { BigInteger a=new BigInteger(n); a=a.add(BigInteger.ONE); BigInteger two=BigInteger.valueOf(2); while(a.mod(two).equals(BigInteger.ZERO)){ a=a.divide(two); } return a.equals(BigInteger.ONE); } }
C题——基环树+暴力
首先n个点n条边的连通无向无权图(不存在自环)这个条件就保证了这个图只有一个环,即是一棵基环树。
因为n比较小,我们可以直接枚举删除环上面的每条边,重现建图后用树形dp求出直径,同时更新维护直径最大值就行了。
#include <bits/stdc++.h> #define PI atan(1.0)*4 #define rp(i,s,t) for (int i = (s); i <= (t); i++) #define RP(i,t,s) for (int i = (t); i >= (s); i--) #define sc(x) scanf("%d",&x) #define scl(x) scanf("%lld",&x) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define pll pair<ll,ll> #define pil pair<int,ll> #define m_p make_pair #define p_b push_back #define ins insert #define era erase #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define dg if(debug) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"; using namespace std; int debug = 0; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll lcm(ll a,ll b){ return a/gcd(a,b)*b; } inline int read(){ int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; } const int N = 5e3+7; vector<pii> G[N]; int inq[N],in[N]; int vis[N]; int n; int dp[N]; int ans; void dfs(int u,int fa){ dp[u]=1; int pre=0; for(auto vv:G[u]){ int v=vv.first,id=vv.second; if(inq[id]||v==fa) continue; dfs(v,u); dp[u]=max(dp[u],dp[v]+1); ans=max(ans,dp[u]); ans=max(dp[v]+pre+1,ans); pre=max(pre,dp[v]); } } class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n int整型 * @param u int整型vector * @param v int整型vector * @return int整型 */ int MaxDiameter(int n, vector<int>& u, vector<int>& v) { rp(i,1,n){ G[u[i-1]].p_b(m_p(v[i-1],i)); G[v[i-1]].p_b(m_p(u[i-1],i)); in[u[i-1]]++;in[v[i-1]]++; } queue<int> q; rp(i,1,n) if(in[i]==1) q.push(i); while(!q.empty()){ int u=q.front();q.pop(); for(auto vv:G[u]){ int v=vv.first,id=vv.second; in[v]--; vis[id]=1; if(in[v]==1) q.push(v); } } int res=0; rp(i,1,n){ if(!vis[i]){//枚举在环上的边 ans=0; inq[i]=1; dfs(1,1); inq[i]=0; res=max(res,ans); } } return res-1; } };
第十二场
B题——思维+前缀和思想
首先默认所有音符都不奏响,这样初始答案就是,每当一个音符奏响时,答案加上该音符的总优美程度(额外优美程度+初始优美程度)。
这样当两个音符u和v可以共鸣时,第一次加上u的总优美程度,这时额外优美程度会抵消掉初始的-z,而当第二次加上v的总优美程度,这时就会多一个额外优美程度z。
刚好符合题意,思想有点类似于前缀和,比较巧妙的题。
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * @param n int整型 * @param m int整型 * @param a int整型vector * @param b int整型vector<vector<>> * @return long长整型 */ long long wwork(int n, int m, vector<int>& a, vector<vector<int> >& b) { vector<long long> aa;aa.clear(); for(int i=0;i<a.size();i++) aa.push_back(a[i]); long long res=0; for(int i=0;i<b.size();i++){ aa[b[i][0]-1]+=b[i][2]; aa[b[i][1]-1]+=b[i][2]; res-=b[i][2]; } for(int i=0;i<aa.size();i++){ res+=max(aa[i],0ll); } return res; } };
牛客巅峰训练赛S2钻石&王者——补题
最新推荐文章于 2022-01-17 21:53:49 发布