CF 781
A
题意:
给定\(n\),求四个正整数,满足:
\(4\leq n\leq 10^9,T\leq 10^4\)
题解:
分情况讨论
\(1.n\%4==0\)
\(a=b=c=d=\frac{n}{4}\)
\(2.n\%4==1\)
\(d=1,a=c=\frac{n-1}{4},b=\frac{n-1}{2}\)
\(3.n\%4==2\)
\(c=d=1,a=\frac{n-2}{2}-1,b=\frac{n-2}{2}+1\)
\(4.n\%4==3\)
\(d=1,a=c=2,b=n-5\)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10,mod=1e9+7,inf=INT_MAX;
int n;
int a,b,c,d;
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n;
int op=n;
if(n%4==0)
{
a=b=c=d=n/4;
}
if(n%4==1)
{
d=1;
--n;
a=c=n/4;
b=2*a;
}
if(n%4==2)
{
c=d=1;
n-=2;
a=n/2-1;
b=n/2+1;
}
if(n%4==3)
{
d=1;
a=c=2;
b=n-5;
//n*4+2
}
cout<<a<<' '<<b<<' '<<c<<' '<<d<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
10
4
5
6
7
8
9
10
11
12
13
*/
B
题意:
给定一个数组,你可以在任意时刻,任意次克隆它,然后和克隆后数组中的元素互换,问最少克隆多少次才能把原数组全都换成同一元素?
\(n\leq 10^5\)
题解:
都换成众数最优化。
假如众数是\(x\)个,那么克隆一次可以获得\(2*x\)个,两次可以获得\(2^2*x\),克隆\(k\)次可以获得\(2^k*x\)个。
找众数:先排序,然后找连续相同的。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10,mod=1e9+7,inf=INT_MAX;
int n,ans,sum,pre;
int a[N];
inline void work(int len)
{
sum=n-len;
int tot=len;
while(tot<n) tot<<=1,++sum;
ans=min(ans,sum);
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n;ans=inf;
for(int i=1;i<=n;++i)
{
cin>>a[i];
}
pre=1;
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
{
if(a[i]==a[pre]) continue;
work(i-pre);
pre=i;
}
work(n-pre+1);
cout<<ans<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
C
题意:
给一颗树,一开始所有节点都是健康的。
现在你轮流进行两个操作:
\(1.\)如果一个节点至少一个儿子被感染了,那么可以选择这个节点的一个其他儿子感染。
\(2.\)选一个健康节点,注入病毒。
求最少多少轮把所有节点感染。
\(n\leq 2*10^5\)
题解:
阿哲,这个一个把我骗到了。
我们先把每个节点提出来,权值就是它的儿子数,根节点要单算一个\(1\),然后从大到小排序。
接下来我们不知道我们应该放任已经被感染去感染多少个孩子比较好。
如果提前知道我们想在多少轮内结束战斗,就好了。
所以考虑二分答案!
这样就可以知道我们在感染某个节点后,它能传染多少个兄弟了,如果在预期轮数之前兄弟没被感染完,就需要我们加把火。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10,mod=1e9+7,inf=INT_MAX;
int n,ans;
int a[N],num;
vector<int> eg[N];
int st[N],top;
inline void dfs(int now,int fa)
{
int sum=0;
for(int t:eg[now])
{
if(t==fa) continue;
dfs(t,now);
++sum;
}
if(sum) st[++top]=sum;
}
inline bool cmp(int a,int b)
{
return a>b;
}
inline bool check(int x)
{
int tot=0;
for(int i=1;i<=top;++i)
{
++tot;
a[i]=st[i]-1-(x-i);
if(a[i]<=0) continue;
tot+=a[i];
}
return tot<=x;
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n;top=0;
st[++top]=1;ans=0;
for(int i=1;i<=n;++i) eg[i].clear();
for(int i=2;i<=n;++i)
{
int fa;cin>>fa;
eg[fa].push_back(i);
eg[i].push_back(fa);
}
dfs(1,0);
sort(st+1,st+top+1,cmp);
int l=top,r=n;
while(l<=r)
{
//cout<<mid<<"!!!!!!!!!!!!!!!"<<endl;
if(check(mid)) r=mid-1;
else l=mid+1;
}
cout<<r+1<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
6
1 1 1 1 1
*/
D
题意:
交互题:
我有一个数字\(x\),但我不告诉你。
每次你可以问我两个不一样的正整数\(a,b\),我会告诉你\(gcd(x+a,x+b)\)的值。
最多\(30\)次后,猜猜数字\(x\)是几~
\(x\leq 10^9\)
题解:
这个\(30\)一猜就是在确定二进制上的每一位。
一开始直接询问\(a=1,b=3\)。
如果返回奇数,那说明\(x\)是偶数,否则\(x\)是奇数,读者自证不难(雾)
考虑已经确定了\(x\)的最小的\(k-1\)个二进制位,它们代表的数字是\(tmp\),现在想要知道\(x\)的第\(k\)个二进制位上是\(0\)还是\(1\)
我们直接设一个\(a=2^k-tmp,b=3*2^k-tmp\)
这样\(x+a\)会直接把后\(k-1\)位给消掉,消到第\(k\)位上去,\(x+b\)同理。
这样如果询问结果的二进制最低位是\(k\),则\(x\)的第\(k\)位是\(0\),否则第\(k\)位是\(1\)。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10,mod=1e9+7,inf=INT_MAX;
int n;
int a[N];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
int tmp=0;int g=0;
cout<<"? 1 3"<<endl;
cin>>g;
if(g&1) tmp=0;
else tmp=1;
for(int k=1;k<30;++k)
{
int a=(1ll<<k),b=3*a;
a-=tmp,b-=tmp;
cout<<"? "<<a<<' '<<b<<endl;
cin>>g;
if(lowbit(g)!=(1ll<<k)) tmp|=(1ll<<k);
}
cout<<"! "<<tmp<<endl;
// 11
// 11
// 101011011101
// 101011001101
}
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
E
题意:
给定一个数组\(A\),每次给定\(l,r\),求\(a_l\sim a_r\)中,\(min\{a_i|a_j\}\)
\(n,m\leq 10^5,a_i<2^{30}\)
题解:
逆天。
结论:如果值域为\([0,2^k)\),答案一定在最小的\(k+1\)个数字中诞生。
证明:
考虑数学归纳:如果\(k=1\),那么只有两个数字。
如果\(p\)满足,考虑第\(p+1\)位。
如果有超过\(2\)个数字第\(p+1\)位是\(0\),那么最终答案的第\(p+1\)位应该是\(0\),然后再让前\(p\)位满足。答案在第\(p+1\)位是\(0\)的最小的\(p+1\)个数字中,也就是最小的\(p+1\)个数字中。
如果只有一个数字第\(p+1\)位是\(1\),那么最终答案的第\(p+1\)位应该是\(1\),先把第\(p+1\)位是\(1\)的最小的\(p+1\)个数字找到,再加上是\(0\)的这个数字,一共\(p+2\)个。
如果所有数字第\(p+1\)位都是\(1\),还是只要\(p+1\)个数字。
得证。
所以答案一定是在区间最小的\(31\)个数字里面。
求出这个数字可以用主席树,然后算答案\(31*31\)暴力
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=1e5+10,mod=1e9+7,inf=INT_MAX;
int n,m,num;
int a[N],c[N];
int rt[N];
int st[N],top;
struct hjt_tree
{
int ans[N*31],tot;
int son[N*31][2];
inline void update(int x,int l,int r,int pre,int &p)
{
p=++tot;
ans[p]=ans[pre]+1;
if(l==r) return;
son[p][0]=son[pre][0],son[p][1]=son[pre][1];
if(x<=mid) update(x,l,mid,son[pre][0],son[p][0]);
else update(x,mid+1,r,son[pre][1],son[p][1]);
}
inline int query(int l,int r,int pre,int p,int k)
{
//cout<<l<<' '<<r<<' '<<k<<"!!"<<endl;
//cout<<son[p][0]<<' '<<son[pre][0]<<"!!!!!!!!!!!!!!!!!!!!"<<endl;
//cout<<ans[son[p][0]]-ans[son[pre][0]]<<"!!!"<<endl;
if(l==r) return l;
if(ans[son[p][0]]-ans[son[pre][0]]>=k) return query(l,mid,son[pre][0],son[p][0],k);
return query(mid+1,r,son[pre][1],son[p][1],k-(ans[son[p][0]]-ans[son[pre][0]]));
}
}T;
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int TT;cin>>TT;
while(TT--)
{
cin>>n;T.tot=0;num=0;
for(int i=1;i<=n;++i)
{
cin>>a[i];
c[++num]=a[i];
}
sort(c+1,c+num+1);
num=unique(c+1,c+num+1)-c-1;
for(int i=1;i<=n;++i)
{
a[i]=lower_bound(c+1,c+num+1,a[i])-c;
T.update(a[i],1,num,rt[i-1],rt[i]);
}
cin>>m;
for(int i=1;i<=m;++i)
{
int l,r;cin>>l>>r;
int maxn=min(31,r-l+1);
for(int j=1;j<=maxn;++j)
{
st[j]=T.query(1,num,rt[l-1],rt[r],j);
st[j]=c[st[j]];
//cout<<st[j]<<"!!!!"<<endl;
}
int ans=inf;
for(int j=1;j<=maxn;++j)
{
for(int k=j+1;k<=maxn;++k) ans=min(ans,st[j]|st[k]);
}
cout<<ans<<'\n';
}
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
5
6 1 3 2 1
1
1 2
*/