Codeforces Round #507 (Div. 2) D~E

5 篇文章 0 订阅
1 篇文章 0 订阅

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值