A.牛牛的mex
n , q ≤ 1 0 5 , 0 ≤ a i < n 且 a i 互 不 相 同 n,q≤10 ^5,0≤a i<n 且 a_i互不相同 n,q≤105,0≤ai<n且ai互不相同
后面两个条件非常重要,通过后面两个条件将问题转化为一个区间内最小未出现的自然数就等于不在这个区间内最小出现的自然数对于区间 [ l , r ] [l,r] [l,r]只需要算出 [ 1 , l − 1 ] 和 [ r + 1 , n ] [1,l-1]和[r+1,n] [1,l−1]和[r+1,n]区间最小出现的自然数即可。直接预处理前缀后缀注意边界由于 0 ≤ a i < n 0\leq a_i<n 0≤ai<n只需让pre[0]=suc[n+1]=n
即可
//O(n)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N],pre[N],suc[N];
int n,q;
int main()
{
cin>>n>>q;
pre[0]=suc[n+1]=n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) pre[i]=min(pre[i-1],a[i]);
for(int i=n;i;i--) suc[i]=min(suc[i+1],a[i]);
while(q--)
{
int l,r;
cin>>l>>r;
cout<<min(pre[l-1],suc[r+1])<<'\n';
}
return 0;
}
由于日常眼瞎非常容易看不见上述重要条件,因此不如上优雅暴力——莫队
//O(nsqrt(n))
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=100010;
int cnt[N],sz,pos[N];
struct node
{
int l,r,id;
bool operator<(const node& o) const
{
if(pos[l]==pos[o.l]) return r<o.r;
else return pos[l]<pos[o.l];
}
}q[N];
int a[N],n,m;
int ans[N],res;
void add(int k)
{
cnt[a[k]]++;
while(cnt[res]) res++;
}
void sub(int k)
{
cnt[a[k]]--;
if(!cnt[a[k]]&&a[k]<=res) res=a[k];
}
int main()
{
cin>>n>>m;
sz=sqrt(n);
for(int i=1;i<=n;i++)
{
cin>>a[i];
pos[i]=i/sz;
}
for(int i=1;i<=m;i++)
{
cin>>q[i].l>>q[i].r;
q[i].id=i;
}
sort(q+1,q+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
while(l<q[i].l) sub(l++);
while(l>q[i].l) add(--l);
while(r<q[i].r) add(++r);
while(r>q[i].r) sub(r--);
ans[q[i].id]=res;
}
for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
return 0;
}
B.牛牛的算术
发现当 n ≥ 199999 n \geq 199999 n≥199999 时答案必为 0 0 0。
于是我们只需要预处理 n < 199999 n < 199999 n<199999的答案即可。
预处理前缀和和前缀积直接算答案。
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
const ll mod=199999;
ll s1[N],s2[N];
string p="199999";
void init()
{
for(int i=1;i<mod;i++)
s1[i]=(s1[i-1]+1ll*i*(i+1)/2*i%mod)%mod;
s2[0]=1;
for(int i=1;i<mod;i++)
s2[i]=s2[i-1]*i%mod*s1[i]%mod;
}
int main()
{
int T=1;
init();
cin>>T;
while(T--)
{
string a;
cin>>a;
if(a.size()>6||a.size()==6&&a>p)
{
cout<<0<<'\n';
continue;
}
reverse(a.begin(),a.end());
int base=1,n=0;
for(auto t:a)
{
n+=base*int(t-'0');
base*=10;
}
cout<<s2[n]<<'\n';
}
return 0;
}
C.牛牛的无向图
不难看出所求就是通过长度不大于L的边相互可达点的数量
对于大于L的边我们可以看作无这条边,我们用并查集维护连通块点的数量。
把询问和边放在一起排序然后处理即可
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010,M=500010;
struct node
{
int op;
int a,b,w;
int id;
bool operator<(const node& o)const
{
if(w==o.w) return op<o.op;
return w<o.w;
}
}e[2*M];
int n,m;
int q[M],k;
ll ans[M],now;
unsigned int SA, SB, SC; int LIM;
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, &k, &SA, &SB, &SC, &LIM);
for(int i = 1; i <= m; i++){
e[i].a = rng61() % n + 1;
e[i].b = rng61() % n + 1;
e[i].w = rng61() % LIM;
}
for(int i = 1; i <= k; i++){
q[i] = rng61() % LIM;
}
}
int p[N],sz[N];
int find(int x)
{
return x==p[x]?x:p[x]=find(p[x]);
}
int main()
{
gen();
for(int i=1;i<=m;i++) e[i].op=1;
for(int i=1;i<=n;i++) p[i]=i,sz[i]=1;
for(int i=1;i<=k;i++)
{
e[i+m].op=2;
e[i+m].w=q[i];
e[i+m].id=i;
}
sort(e+1,e+1+m+k);
for(int i=1;i<=m+k;i++)
{
if(e[i].op==1)
{
int pa=find(e[i].a),pb=find(e[i].b);
if(pa!=pb)
{
p[pa]=pb;
now+=1ll*sz[pa]*sz[pb];
sz[pb]+=sz[pa];
}
}
else
ans[e[i].id]=now;
}
ll res=0;
for(int i=1;i<=k;i++) res^=ans[i];
cout<<res<<'\n';
return 0;
}
牛客网的题目感觉挺不错的,但是评测机就很让人落泪啊
要加油哦~