美团CodeM复赛题解
配对游戏
思路
设f[i][j]表示前i个人中,还有j个向右看的人可以消除,这种状态的期望消除数量, g[i][j]为到达这种状态的概率。
考虑第i + 1的朝向,如果是向右,那么f[i + 1][j + 1] ← f[i][j]/2,
g[i + 1][j + 1] ← g[i][j]/2,如果是向左,那么
f[i + 1][j − 1] ← f[i][j]/2 + g[i][j],g[i + 1][j − 1] ← g[i][j]/2。(注意 j = 0的情况)
代码
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int n,i,j,k;
double f[2005][2005],g[2005][2005],ans;
int main()
{
scanf("%d",&n);
g[0][0]=1;
for(i=0;i<n;++i)
for(j=0;j<=i;++j)
{
f[i+1][j+1]+=f[i][j]/2;g[i+1][j+1]+=g[i][j]/2;
if(j)f[i+1][j-1]+=f[i][j]/2+g[i][j],g[i+1][j-1]+=g[i][j]/2;
else f[i+1][j]+=f[i][j]/2,g[i+1][j]+=g[i][j]/2;
}
ans=n;
for(i=0;i<=n;++i)ans-=f[n][i];
printf("%.3lf\n",ans);
}
城市网络
思路
考虑离线处理,可以对每个询问,建出一个新点,作为叶子挂在起点下面。
然后建一棵新树,对于每个点,求出它的祖先中,第一个比它权值大的,作为它在新树中 的父亲。
考虑在新树上处理询问,可以倍增找出需要跳多少点。
代码
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
int n,q,i,j,k,u,v,l,r,ans;
int a[200005],id[200005];
int tid[200005],t[200005][20],cnt;
int tfa[200005],fa[200005],deep[200005],aim[200005];
int son[200005],Next[400005],ed[400005],tot;
bool vis[200005];
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
inline bool cmp(const int &x,const int &y){return a[x]<a[y];}
void dfs(int x)
{
vis[x]=true;tid[++cnt]=x;
for(int i=son[x];i;i=Next[i])
if(!vis[ed[i]])
{
tfa[ed[i]]=x;
deep[ed[i]]=deep[x]+1;
dfs(ed[i]);
}
}
int main()
{
scanf("%d%d",&n,&q);
for(i=1;i<=n;++i)scanf("%d",&a[i]);
for(i=1;i<n;++i)
{
scanf("%d%d",&u,&v);
++tot;Next[tot]=son[u];son[u]=tot;ed[tot]=v;
++tot;Next[tot]=son[v];son[v]=tot;ed[tot]=u;
}
deep[1]=1;dfs(1);
for(i=1;i<=q;++i)tid[++cnt]=n+i;
for(i=1;i<=n+q;++i)fa[i]=id[i]=i;
for(i=1;i<=q;++i)scanf("%d%d%d",&tfa[n+i],&aim[n+i],&a[n+i]),deep[n+i]=deep[tfa[n+i]]+1;
sort(id+1,id+n+q+1,cmp);
for(l=1;l<=n+q;l=r+1)
{
for(r=l;r<n+q&&a[id[r+1]]==a[id[l]];++r);
for(i=l;i<=r;++i)u=id[i],fa[u]=tfa[u];
for(i=l;i<=r;++i)u=id[i],t[u][0]=get(u);
}
for(i=1;i<=n+q;++i)
{
u=tid[i];
for(j=0;t[u][j];++j)
t[u][j+1]=t[t[u][j]][j];
}
for(i=n+1;i<=n+q;++i)
{
ans=0;u=i;
for(j=18;j>=0;--j)
if(deep[t[u][j]]>=deep[aim[i]])
u=t[u][j],ans+=1<<j;
printf("%d\n",ans);
}
}
神秘代码
思路
N 个点N 条边的图一定是环套外向树。考虑将环上的所有方程都取出来(设共有x 个),那 么这些方程构成了一个x个方程x个变量且首尾相接的方程组。考虑每次用第一个方程去 消,消去相同的一部分,消完后将会得到一个仅关于第一个变量的方程。由于题目保证解 唯一,所以最后的方程是ax ≡ b (mod p)的形式,通过求a的逆元就可以求得第一个变 量的值。
求得一个值后,通过bfs递推就可以得到剩余每个位置的xi 了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#include<cmath>
#include<string>
#define ls (t<<1)
#define rs ((t<<1)+1)
#define mid ((l+r)>>1)
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define N 300005
#define M 200005
#define seed 23333
using namespace std;
int i,j,m,n,p,k,vis[N],deg[N],Q[N],cy[N],x,y,fa[N],A[N];
long long a,b,c;
struct Node{
long long a,b,c;
};
Node fx[N],E[N];
vector<pair<int,Node> >v[N];
void Find_Cycle()
{
for (i=1;i<=n;++i) if (deg[i]==1) Q[++Q[0]]=i,vis[i]=1;
int l;
for (l=1;l<=Q[0];++l)
{
int p=Q[l];
for (i=0;i<(int)v[p].size();++i)
{
int k=v[p][i].fi; --deg[k];
if (!vis[k]) fa[p]=k,E[p]=v[p][i].se;
if (deg[k]==1)
{
Q[++Q[0]]=k;
vis[k]=1;
}
}
}
for (i=1;i<=n;++i) if (!vis[i]) break;
for (;!vis[i];)
{
cy[++cy[0]]=i;
vis[i]=1;
for (j=0;j<(int)v[i].size();++j)
{
int k=v[i][j].fi;
if (!vis[k])
{
fx[cy[0]]=v[i][j].se;
i=k;
break;
}
}
}
}
int power(int x,int y)
{
int sum=1;
for (;y;y>>=1)
{
if (y&1) sum=1ll*sum*x%p;
x=1ll*x*x%p;
}
return sum;
}
int main()
{
scanf("%d%d",&n,&p);
for (i=1;i<=n;++i)
{
scanf("%d%d%lld%lld%lld",&x,&y,&a,&b,&c);
v[x].pb(mk(y,(Node){a,b,c}));
v[y].pb(mk(x,(Node){b,a,c}));
deg[x]++; deg[y]++;
}
Find_Cycle();
Node now=fx[1];
k=cy[cy[0]];
for (i=0;i<(int)v[k].size();++i)
{
int p=v[k][i].fi;
if (p==cy[1]) fx[cy[0]]=v[k][i].se;
}
for (i=2;i<=cy[0];++i)
{
now.a=(now.a*fx[i].a%p+p)%p;
now.c=((now.c*fx[i].a-fx[i].c*now.b)%p+p)%p;
now.b=((p-fx[i].b)*now.b%p+p)%p;
}
(now.a+=now.b)%=p;
if (now.a==0) puts("cao"); else A[cy[1]]=1ll*now.c*power(now.a,p-2)%p;
for (i=2;i<=cy[0];++i) A[cy[i]]=1ll*(fx[i-1].c-1ll*A[cy[i-1]]*fx[i-1].a%p+p)*power(fx[i-1].b,p-2)%p;
for (i=Q[0];i;--i)
{
int k=Q[i];
A[k]=1ll*(E[k].c-1ll*A[fa[k]]*E[k].b%p+p)*power(E[k].a,p-2)%p;
}
for (i=1;i<=n;++i) printf("%d\n",A[i]);
}
排列
思路
代码
#include<algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <vector>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
const int N=100005,Mod=1e9+7;
int n,x[N],y[N],f[N],Num[N],s[N],inc[N],ans[N];
void Add(int x,int k)
{
while(x<=n)
s[x]=(s[x]+k)%Mod,x+=x&-x;
}
int Sum(int x)
{
int ss=0;
while(x)
ss=(ss+s[x])%Mod,x-=x&-x;
return ss;
}
int main()
{
scanf("%d",&n);
for(int i=1,a,b;i<=n;i++)
scanf("%d%d",&a,&b),x[i]=a,y[a]=b;
for(int i=n;i>=1;i--)
Num[i]=n-i-Sum(y[i]),Add(y[i],1);
inc[0]=inc[1]=1;
int fac=1;
for(int i=2;i<=n;i++)
inc[i]=(Mod-(Mod/i)*(LL)inc[Mod%i]%Mod)%Mod,fac=fac*(LL)(i-1)%Mod;
memset(s,0,sizeof s);
int count=0;
for(int i=1;i<=n;i++)
{
f[i]=(LL)(Sum(y[i])+1)*inc[Num[i]]%Mod;
Add(y[i],f[i]);
if(Num[i]==0)ans[i]=f[i]*(LL)fac%Mod,count++;
else ans[i]=0;
}
for(int i=1;i<=n;++i)printf("%d\n",ans[x[i]]);
// cout<<count<<endl;
return 0;
}
Pairsum
思路
本题是一道趣味的构造题,构造方法不唯一,下面是作者的方法。
选取一个最大的素数p使得2p^2<=n,然后构造数列a[i] = 2ip+(i^2 mod p),其中i=1,2,…p。如果a[i] = a[j],那么有2ip=2jp从而i=j. 根据素数定理,p距离n不会太远,所以数列长度p大约是(n/2)^(1/2),远大于n^(1/2)/2。但是一些较小的n会出现问题,所以进行特判解决。
下面证明数列a的pairwise sum是distinct的。如果a[i]+a[j] = a[k]+a[l],那么考虑除以p的结果和余数,分别有2i+2j=2k+2l以及i^2+j^2==k^2+l^2 (mod p)。于是i–k =l–j且i^2-k^2 == l^2-j^2 (mod p)。显然i-k非0且mod p非0,于是得到i+k==l+j (mod p). 显然也有i-k==l-j (mod p),于是i==l (mod p),k==j (mod p),结合i,j,k,l属于{1, 2, …, p},于是有i=l,k=j。
另外,也可能有一些基于分治或者搜索的其他搜索,期待能看到一些有趣的解法。
随机方法构造的数列长度大约在n^(1/3)左右。
当我思考这个问题的时候并不知道这是一个被研究的组合问题,叫做Sidon set. 这个问题已经在2010年基本解决。实际上很多年前Paul Erdos和Pal Turan曾经证明对于n,答案最多是n^(1/2) + O(n^(1/4))。Singer有一个x^(1/2) (1-o(1))的构造。,细节可以看wikipedia。
代码
#include <iostream>
#include <cassert>
#include <stdio.h>
using namespace std;
bool isprime(int n) {
if (n == 2) return true;
for (int i = 2; i * i <= n; ++ i)
if (n % i == 0) return false;
return true;
}
void solve(int n) {
if (n <= 4) { cout << "1\n1" << endl; return; }
if (n <= 16) { cout << "2\n1 2" << endl; return; }
if (n <= 36) { cout << "3\n1 2 3" << endl; return; }
if (n <= 64) { cout << "4\n1 2 4 8" << endl; return; }
if (197 <= n && n <= 256) {cout << "8\n1 2 4 8 16 32 64 128" << endl; return; }
int p = 0;
for (int i = 2; 2 * i * i <= n; ++ i)
if (isprime(i)) p = i;
assert(4 * p * p >= n);
cout << p << endl;
for (int i = 1; i <= p; ++ i)
cout << 2 * i * p + ((i * i) % p) << " ";
cout << endl;
}
int main() {
int n;
cin >> n;
solve(n);
return 0;
}
通信
思路
http://immortalco.blog.uoj.ac/blog/2102
代码
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <string>
#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define REP(i, x, y) for(int i = (int)x; i <= (int)y; i ++)
#define FOR(i, x, y) for(int i = (int)x; i < (int)y; i ++)
#define PER(i, x, y) for(int i = (int)x; i >= (int)y; i --)
#define trace(x) cerr << #x << " " << x << endl;
#define dprintf(...) fprintf(stderr, __VA__ARGS__)
#define dln() fprintf(stderr, "\n")
using namespace std;
typedef long long LL;
typedef long double db;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPI;
const int N = 800005;
const int P = 1e9 + 7;
const int inf = 1e9;
const LL Inf = 1e15;
const int S = 1000005;
char bf[S],*p1=bf,*p2=bf;
#define nc() (p1==p2&&(p2=(p1=bf)+fread(bf,1,S,stdin),p2==p1)?-1:*p1++)
inline int IN(){
int x=0;char ch=nc();for(;ch<'0'||ch>'9';ch=nc());
for(;ch<='9'&&ch>='0';x=x*10+ch-48,ch=nc());return x;
}
inline int Pow(int x, int y, int p){
int an = 1;
for(; y; y >>= 1, x = (LL)x * x % p) if(y & 1) an = (LL)an * x % p;
return an;
}
void renew(int &x, int y){
x += y;
if(x < 0) x += P;
else if(x >= P) x -= P;
}
void setIO(string a){
#ifndef LOCAL
freopen((a + ".in").c_str(), "r", stdin);
freopen((a + ".out").c_str(), "w", stdout);
#else
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
}
template<typename T> inline void chkmin(T &a, const T &b) {if(a > b) a = b;}
template<typename T> inline void chkmax(T &a, const T &b) {if(a < b) a = b;}
typedef int ones[N];
typedef unsigned int u32;
bool st;
int n, top;
LL Lcost, Rcost;
ones X, Lx, Rx, stk;
struct Info{
LL Min;
u32 ways;
Info() = default;
Info(LL a, u32 b) : Min(a), ways(b) {}
};
Info operator +(const Info &a, const Info &b) {
return (a.Min < b.Min) ? a : ((a.Min > b.Min) ? b : Info(a.Min, a.ways + b.ways));
}
Info operator +(const Info &a, const LL &b) {
return Info(a.Min + b, a.ways);
}
Info A[16551425], B[16551425], C[16551425], D[16551425];
Info *curA = A, *curB = B, *curC = C, *curD = D;
Info dp[N];
//dp[i] = min(j < i) dp[j] + max(Ai - Ax, Bx - Bj)
//m1, x1 max = i
//m2, x2 max = j
struct segnode{
Info *m1, *m2, *x1, *x2;
int xl, xr, xmd;
void init(int l, int r){
xl = l;
xr = r;
xmd = (l + r) / 2;
m1 = curA; curA += r - l + 2;
m2 = curB; curB += r - l + 2;
x1 = curC; curC += r - l + 2;
x2 = curD; curD += r - l + 2;
REP(i, l, r) x1[i - l + 1].Min = 1LL << 60;
REP(i, l, r) x2[i - l + 1].Min = 1LL << 60;
}
void work(){
REP(i, xl, xr) m1[i - xl + 1] = dp[i];
REP(i, xl, xr) m2[i - xl + 1] = dp[i] + (-Rcost * i);
REP(i, xmd + 2 - xl + 1, xr - xl + 1) m1[i] = m1[i - 1] + m1[i], m2[i] = m2[i - 1] + m2[i];
PER(i, xmd - 1 - xl + 1, 1) m1[i] = m1[i + 1] + m1[i], m2[i] = m2[i + 1] + m2[i];
}
Info askopt1(int l, int r){
return m1[l - xl + 1] + m1[r - xl + 1];
}
Info askopt2(int l, int r){
return m2[l - xl + 1] + m2[r - xl + 1];
}
void renew(){
Info d1 = Info(1LL << 60, 0), d2 = d1;
REP(i, xl, xmd){
d1 = d1 + x1[i - xl + 1];
d2 = d2 + x2[i - xl + 1];
dp[i] = dp[i] + (d1 + (Lcost * i));
dp[i] = dp[i] + d2;
}
d1 = Info(1LL << 60, 0), d2 = d1;
PER(i, xr, xmd + 1){
d1 = d1 + x1[i - xl + 1];
d2 = d2 + x2[i - xl + 1];
dp[i] = dp[i] + (d1 + (Lcost * i));
dp[i] = dp[i] + d2;
}
}
void tag1(int l, int r, Info v){
// assert(xl <= l && l <= xmd && xmd < r && r <= xr);
l = l - xl + 1;
r = r - xl + 1;
x1[l] = x1[l] + v;
x1[r] = x1[r] + v;
}
void tag2(int l, int r, Info v){
// assert(xl <= l && l <= xmd && xmd < r && r <= xr);
l = l - xl + 1;
r = r - xl + 1;
x2[l] = x2[l] + v;
x2[r] = x2[r] + v;
}
} T[2100005];
int ID[N];
#define LEADZERO(x) (__builtin_clz(x) - 2)
#define HIGH_BIT(x) (30 - LEADZERO(x))
#define findv(l, r) ((ID[l]) >> HIGH_BIT(ID[l] ^ ID[r]))
Info ask1(int l, int r){
if(l == r) return dp[l];
int x = findv(l, r);
return T[x].askopt1(l, r);
}
Info ask2(int l, int r){
if(l == r) return dp[l] + (-Rcost * l);
int x = findv(l, r);
return T[x].askopt2(l, r);
}
void Tag1(int l, int r, Info v){
if(l == r){
dp[l] = dp[l] + (v + Lcost * l);
return;
}
int x = findv(l, r);
T[x].tag1(l, r, v);
}
void Tag2(int l, int r, Info v){
if(l == r){
dp[l] = dp[l] + v;
return;
}
int x = findv(l, r);
T[x].tag2(l, r, v);
}
int mxp[N];
void work(int x, int L, int R){
if(L == R) return;
int xl = L, xr = R, xmd = (L + R) / 2;
T[x].renew();
work(x << 1, L, xmd);
mxp[xmd + 1] = xmd + 1;
REP(i, xmd + 2, xr){
mxp[i] = mxp[i - 1];
if(X[i] > X[mxp[i]]) mxp[i] = i;
}
mxp[xmd] = xmd;
PER(i, xmd - 1, xl){
mxp[i] = mxp[i + 1];
if(X[i] > X[mxp[i]]) mxp[i] = i;
}
REP(i, xmd + 1, xr){
LL d = Lcost * (i - mxp[i]) / Rcost, pos1 = mxp[i] - d;
int Lt = max(Lx[mxp[i]] + 1, xl);
pos1 = max(pos1, (LL)Lt);
if(pos1 <= xmd) dp[i] = dp[i] + (ask1(pos1, xmd) + Lcost * (i - mxp[i]));
pos1 = min(pos1 - 1, (LL)xmd);
if(Lt <= pos1) dp[i] = dp[i] + (ask2(Lt, pos1) + Rcost * mxp[i]);
}
PER(i, xmd, xl){
int Rt = min(Rx[mxp[i]] - 1, xr);
LL pos1 = Rcost * (mxp[i] - i) / Lcost + mxp[i];
pos1 = min(pos1, (LL)Rt);
if(xmd + 1 <= pos1) Tag2(xmd + 1, pos1, dp[i] + Rcost * (mxp[i] - i));
pos1 = max(pos1 + 1, xmd + 1LL);
if(pos1 <= Rt) Tag1(pos1, Rt, dp[i] + (-Lcost * mxp[i]));
}
work(x * 2 + 1, xmd + 1, R);
T[x].work();
}
void build(int x, int L, int R){
if(L == R){
ID[L] = x << LEADZERO(x);
return;
}
int md = (L + R) >> 1;
build(x << 1, L, md);
build(x * 2 + 1, md + 1, R);
T[x].init(L, R);
}
bool en;
int main(){
//cerr << (&en - &st) / 1048576. << endl;
n = IN();
Lcost = IN();
Rcost = IN();
REP(i, 1, n) X[i] = IN();
REP(i, 1, n) dp[i] = Info(1LL << 60, 0);
dp[1] = Info(0, 1);
REP(i, 1, n) Rx[i] = n + 1;
REP(i, 1, n){
while(top && X[i] > X[stk[top]]){
Rx[stk[top]] = i;
top --;
}
if(top) Lx[i] = stk[top];
stk[++top] = i;
}
build(1, 1, n);
work(1, 1, n);
LL Ans1 = 0;
u32 Ans2 = 0;
REP(i, 1, n) Ans1 ^= dp[i].Min;
REP(i, 1, n) Ans2 ^= dp[i].ways;
cout << Ans1 << endl << Ans2 << endl;
return 0;
}#美团#