牛客练习赛68(A B C)
Practice link: https://ac.nowcoder.com/acm/contest/7079
A : 牛牛的mex
思路:考虑给出条件:$0<=a{}_{n}<=n-1$ 且每个数字不相同,因此区间 $\left[ l,r\right]$ 的mex就是区间 $\left[ 1,l-1\right]$ 和 $\left[ r+1,n\right]$ 中的最小值。
只需要从前到后 和 从后到前维护两个min。
代码:
#include<bits/stdc++.h> #define ll long long #define MOD 1000000007 #define INF 0x3f3f3f3f3f #define mem(a,x) memset(a,x,sizeof(a)) #define _for(i,a,b) for(int i=a; i< b; i++) #define _rep(i,a,b) for(int i=a; i<=b; i++) #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const int MAXN = 200005 ; inline int rd() { int res = 0,flag = 0; char ch; if ((ch = getchar()) == '-')flag = 1; else if(ch >= '0' && ch <= '9')res = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9')res = (res<<1) + (res<<3) + (ch - '0'); return flag ? -res : res; } int min1[100005],min2[100005]; int a[100005]; int n,q; int main() { cin>>n>>q; min1[0]=n; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); min1[i]=min(min1[i-1],a[i]); } min2[n+1]=n; for(int i=n;i>=1;i--){ min2[i]=min(min2[i+1],a[i]); } while(q--){ int l,r; scanf("%d %d",&l,&r); printf("%d\n",min(min1[l-1],min2[r+1])); } return 0; }
B:牛牛的算术
思路:考虑公式
$\prod^{n}_{i=1} \sum^{i}_{j=1} \sum^{j}_{k=1} $ = $\prod^{n}_{i=1} i\sum^{i}_{j=1} j\sum^{j}_{k=1} k$
那么因为有阶乘,所以当 n>199999 时,答案为 0 。
n<199999时,我们看公式,可以用前缀和计算出 1 ~ 199999 的所有答案,推柿子
ans = $\prod^{n}_{i=1} i\sum^{i}_{j=1} j\frac{j\times (j+1)}{2}$
也就是求一个 $x[n]=\sum^{n}_{i=1} i\times \frac{i\times (i+1)}{2}$ ,最后 $sum[n]=\prod^{n}_{i=1} i\times x[i]$ .
代码:
#include<bits/stdc++.h> #define ll long long #define MOD 199999 #define INF 0x3f3f3f3f3f #define mem(a,x) memset(a,x,sizeof(a)) #define _for(i,a,b) for(int i=a; i< b; i++) #define _rep(i,a,b) for(int i=a; i<=b; i++) #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const int MAXN = 200005 ; inline int rd() { int res = 0,flag = 0; char ch; if ((ch = getchar()) == '-')flag = 1; else if(ch >= '0' && ch <= '9')res = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9')res = (res<<1) + (res<<3) + (ch - '0'); return flag ? -res : res; } string s; ll x[200005]; ll num[200005]; ll p; int main() { int T; p=1; x[1]=1; for(int i=2;i<199999;i++){ p=p+i; x[i]=(1ll*x[i-1]%MOD)+((p*i)%MOD); x[i]=x[i]%MOD; } num[1]=1; for(int i=2;i<199999;i++){ num[i]=(num[i-1]%MOD)*((i*x[i])%MOD); num[i]=num[i]%MOD; } cin>>T; while(T--){ cin>>s; if(s.length()>=7){ printf("0\n"); }else{ int n=0; for(int i=0;i<s.length();i++){ n=n*10+(s[i]-'0'); //cout<<s[i]<<endl; } //cout<<n<<endl; if(n>=MOD){ printf("0\n"); }else{ printf("%lld\n",num[n]%MOD); } } } return 0; } /* 1 1*1*1 2 (1*1*1)*(2*((1+2)+(1+(1+2)))) 3 (1*1*1)*(2*((1+2)+(1+(1+2))))*(3*((1+2+3)*(1+(1+2)+(1+2+3)))) 7 1*1+2*3 25 1*1+2*3+3*6 (1*1)+(2*(1+2)) (1*1)+(2*(1+2))+(3*(1+2+3)) */
C:牛牛的无向图
思路:首先有 d(u,v)是u到v的最短距离,因此所有点一定是在一棵最小生成树上,其它边都可舍弃。然后考虑最小生成树的加边过程,就是从小到大的,然后我们可以用并查集维护各个连通块,对与某一次合并u和v,因为u和v的距离是w,因此sum[i]就是小于等于w时的数量,也就是sum[i] = sum[i-1]+ siz[fa[u]]*siz[fa[v]] ,其中siz[fa[u]]是点u所在点连通块的点数,最后对每一个L[i]在sum里进行二分查找答案即可。
代码:
//#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define ll long long #define MOD 1000000007 #define INF 0x3f3f3f3f #define mem(a,x) memset(a,x,sizeof(a)) #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const int maxn=500005; inline int rd() { int res = 0,flag = 0; char ch; if ((ch = getchar()) == '-')flag = 1; else if(ch >= '0' && ch <= '9')res = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9')res = (res<<1) + (res<<3) + (ch - '0'); return flag ? -res : res; } /* int head[maxn]; int num=0; struct edg{ int next,to,w; }edge[maxn]; void add_edge(int u,int v,int w) { num++; edge[num].next=head[u];edge[num].to=v;edge[num].w=w;head[u]=num; edge[++num].next=head[v];edge[num].to=u;edge[num].w=w;head[v]=num; } */ //----------- unsigned int SA, SB, SC; int n, m, q, LIM; int L[maxn]; int p[maxn]; int siz[maxn]; ll sum[maxn]; int chosen[maxn]; struct Node{ int u,v,w; }node[maxn]; bool cmp(Node x,Node y) { return x.w<y.w; } unsigned int rng61(){ SA ^= SA << 16; SA ^= SA >> 5; SA ^= SA << 1; unsigned int t = SA; SA = SB; SB = SC; SC ^= t ^ SA; return SC; } void gen(){ scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM); for(int i = 1; i <= m; i++){ node[i].u = rng61() % n + 1; node[i].v = rng61() % n + 1; node[i].w = rng61() % LIM; } for(int i = 1; i <= q; i++){ L[i] = rng61() % LIM; p[i] = L[i]; } } int fa[maxn]; int find(int x) { return (fa[x]==x)?x:fa[x]=find(fa[x]); } void merge(int u,int v) { u=find(u); v=find(v); if(siz[u]>siz[v]){ fa[v]=fa[u]; siz[u]+=siz[v]; }else{ fa[u]=fa[v]; siz[v]+=siz[u]; } } int main() { gen(); for(int i=1;i<=n;i++){ siz[i]=1; fa[i]=i; } sort(p+1,p+1+q); sort(node+1,node+m+1,cmp); int iter=0; sum[0]=0; for(int i=1;i<=m;i++){ if(iter==n-1){ break; } int u=node[i].u,v=node[i].v,w=node[i].w; if(find(u)!=find(v)){ iter++; sum[iter]=sum[iter-1]+1ll*siz[fa[u]]*siz[fa[v]]; chosen[iter]=w; merge(u,v); } } ll ans=0; for(int i=1;i<=q;i++){ int l=0,r=iter,mid; while(l<r){ mid=(l+r+1)>>1; if(L[i]>=chosen[mid]){ l=mid; }else{ r=mid-1; } } ans^=sum[l]; } printf("%lld\n",ans); return 0; }