D
这个题,首先可以发现是没法通过二分确定准确位置的,其次是询问次数很多。纯二分的交互一般都询问次数100以内。所以,可以猜到是卡到一个小区间然后随机询问,根据概率的话,4500次很够了。就还是二分,不过每次二分之后左右边界往两边扩展k的长度,这样的话理论卡得到的最小区间是2*k,实际不知道(雾)。卡到这个区间之后,随机一个区间内的位置,询问,如果Yes,结束,否则重新卡。卡个几十次应该就可以了。
最开始我是只卡了一次,然后一直随机位置猜,每猜一次左右边界拓展k,继续猜。。后来发现概率好像太低了,毕竟4500*10=45000,一个边界45000,区间长度就是90000了。。T到死。
//QWsin
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define out(i,u) for(int i=first[u];i!=-1;i=next[i])
#define repvc(i,vc) for(int i=0,Sz=vc.size();i<Sz;++i)
using namespace std;
const int INF=1<<30;
typedef long long ll;
char s[10];
inline int query(ll x,ll y){
printf("%I64d %I64d\n",x,y);
fflush(stdout);
scanf("%s",s+1);
if(s[1]=='Y') return 1;
return 0;
}
inline ll R(){return (ll)rand();}
ll n,k;
inline void find(ll &l,ll &r)
{
ll mid;l=1;r=n;
while(r-l>=max(5*k,10ll))
{
mid=(l+r)>>1;
if(query(l,mid)) {
l=max(1ll,l-k);
r=min(n,mid+k);
}
else {
l=max(1ll,mid-k);
r=min(n,r+k);
}
}
}
int main()
{
srand(time(0));
cin>>n>>k;k=min(k,n);
while(1)
{
ll l,r;
find(l,r);
l=max(1ll,l-k);r=min(r+k,n);ll len=(r-l+1);
ll t=(1ll*R()*R()*R()*R()+17ll*R()*R()*R()+13*R()*R()+11*R())%len;
t=(t%len+len)%len;
if(query(l+t,l+t)) break;
}
return 0;
}
E
很开心我居然能想出来Div2最后一题了。
首先可以发现x这个参数跟边有关系,假如一条边(u,v),那么x=c[u]^c[v] 时,不能单独选u,v。要么都不选,要么都选。其次,边只有50w条,所以要单独考虑的x最多50w个(其他的x贡献都是
2n
2
n
)。转化一下,现在对于每一个x,有一些限制,要么(u,v)一起选,要么(u,v)都不选,所以就变成了,某个集合要么一起选,要么都不选,所以就变成了,并查集。也就是对于当前x,(u,v)只能一起选的话,就连边,处理完所有限制之后,如果联通块个数是cnt,对答案贡献就是
2cnt
2
c
n
t
。
另外由于要考虑的x数较多,不能每次都O(n)初始化并查集,只需要把上一个x中考虑过的边的两个端点的father重置成自己就可以了。 时间复杂度应该说相当优秀 O(n+m) O ( n + m ) (不过还是跑了300ms是怎么回事)
//QWsin
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define out(i,u) for(int i=first[u];i!=-1;i=next[i])
#define repvc(i,vc) for(int i=0,Sz=vc.size();i<Sz;++i)
inline int read()
{
char ch=getchar();int ret=0;
while(ch<'0' || ch>'9') ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar()) ret=ret*10+ch-'0';
return ret;
}
using namespace std;
const int INF=1<<30;
const int maxn=500000+10;
const int maxm=500000+10;
const int MOD=1e9+7;
typedef long long ll;
ll c[maxn];
struct Edge{int u,v;ll d;Edge(int u=0,int v=0):u(u),v(v){d=c[u]^c[v];}}e[maxm];
vector<Edge>vc;
inline int cmp(const Edge &a,const Edge &b){return a.d<b.d; }
int p[maxn];
int findset(int x){return p[x]==x?x:p[x]=findset(p[x]);}
ll c2[maxn];
int main()
{
int n,m,k;cin>>n>>m>>k;
c2[0]=1;
rep(i,1,n) c2[i]=c2[i-1]*2%MOD;
rep(i,1,n) scanf("%I64d",c+i);
rep(i,1,m) e[i]=Edge(read(),read());
sort(e+1,e+m+1,cmp);
rep(i,1,n) p[i]=i;
int pre=1,cnt=n,vis=0;ll ans=0;
rep(i,1,m+1)
{
if((i!=1 && e[i].d!=e[i-1].d)||i==m+1)
{
ans+=c2[cnt];++vis;
rep(j,pre,i-1) {
p[e[j].u]=e[j].u;
p[e[j].v]=e[j].v;cnt=n;
}
pre=i;
}
if(i==m+1) break;
int pu=findset(e[i].u);
int pv=findset(e[i].v);
if(pu!=pv) p[pu]=pv,--cnt;
}
cout<<(ans+((1ll<<k)-vis)%MOD*c2[n])%MOD<<endl;
return 0;
}