Codeforces 8VC Venture Cup 2017 - Elimination Round

C. PolandBall and Forest

简单并查集,把相关节点合并即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int p[10010];  
int a[10010];

int find(int x){  
    if(x!=p[x])p[x]=find(p[x]);  
    return p[x];  
}  

void _union(int a,int b){  
    int fa=find(a);  
    int fb=find(b);  
    p[fa]=fb; 
}  

int main(){
    int n;
    cin>>n;

    for(int i=1;i<=n;i++){
        p[i] = i;
        cin>>a[i];
    }

    for(int i=1;i<=n;i++){
        _union(i,a[i]);
    }

    set<int> S;

    for(int i=1;i<=n;i++){
        S.insert(find(i));
    }

    cout<<S.size()<<endl;

    return 0;
}

D. PolandBall and Polygon

思考一下,可以发现,每条线的跨度是一样的,所以,只有与当前连线距离小于 k 的已存在的连线,会与当前连线相交,用数据结构维护一下,每次查询符合要求的线已有多少条。注意k>n/2时的情形。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int maxn = 1000010;

int n,k;

int c[maxn];

inline int lowbit(int x){  
    return x&(-x);  
}  

void update(int pos){  
    while(pos<=n){  
        c[pos]++;  
        pos+=lowbit(pos);  
    }  
}  

int query(int pos){  
    int re=0;  
    while(pos){  
        re+=c[pos];  
        pos-=lowbit(pos);  
    }  
    return re;  
}  

int Q(int l,int r){
    if(r >= l){
        return query(r) - query(l-1);
    }else{
        return Q(1,r) + Q(l,n);
    }
}

int main(){
    cin>>n>>k;
    if(k > n/2){
        k = n-k;
    }

    int pos = 0;
    update(1);
    ll ans = 1;

    for(int i=0;i<n;i++){
        int l = pos-k+1;
        int r = pos+k-1;

        pos += k;
        pos %= n;

        if(l<0){
            l+=n;
        }
        if(r>=n){
            r-=n;
        }

        int tmp = Q(l+1,r+1)-1;
        ans += (tmp+1);

        printf("%I64d ",ans);
        update(pos+1);
    }
    return 0;
}

E. PolandBall and White-Red graph

构造题。可以发现,只有 k=2 k=3 时可能有解。 k=2 时,一个大环就是解, k=3 时,造一个仅少一条边的二分图即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int main(){
    int n,k;
    cin>>n>>k;
    if(n==4){
        if(k!=3){
            cout<<-1<<endl;
        }else{
            int half = n/2;
            int half2 = n-half;
            cout<<half*half2 - 1<<endl;
            for(int i=1;i<=half;i++){
                for(int j=half+1;j<=n;j++){
                    if(!(i==1 && j==half+1))printf("%d %d\n",i,j);
                }
            }
        }
    }else if(n>4){
        if(k == 2){
            cout<<n-1<<endl;
            for(int i=1;i<n;i++){
                cout<<i<<" "<<i+1<<endl;
            }
        }else if(k == 3){
            int half = n/2;
            int half2 = n-half;
            cout<<half*half2 - 1<<endl;
            for(int i=1;i<=half;i++){
                for(int j=half+1;j<=n;j++){
                    if(!(i==1 && j==half+1))printf("%d %d\n",i,j);
                }
            }
        }else{
            cout<<-1<<endl;
        }
    }else{
        cout<<-1<<endl;
    }
    return 0;
}

F. PolandBall and Gifts

容易看出,最大值贪心可解,最小值是个背包问题,直接跑多重背包,压到bieset里面优化。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int maxn = 1000010;
int p[maxn];
bool vis[maxn];

int main(){ 
    int n,k;
    cin>>n>>k;

    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
    } 

    vector<int> cir;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            int t = i;
            int len = 0;
            while(!vis[t]){
                vis[t] = 1;
                t = p[t];
                len++;
            }
            cir.push_back(len);
        }
    }
    sort(cir.begin(),cir.end());

    int MAX = 0;
    int kk = k;
    for(int i=0;i<cir.size();i++){
        int tmp = cir[i];
        while(tmp >= 2 && kk > 0){
            MAX += 2;
            kk--;
            tmp -= 2;
        }
    }
    MAX += kk;
    MAX = min(MAX,n);

    vector<int> vec;
    cir.push_back(-1);
    int s = 0;
    for(int i=1;i<cir.size();i++){
        while(cir[i] == cir[i-1]){
            i++;
        }
        int t = i - s;
        s = i;
        int j = 0;
        while(t){
            int tmp = min(1<<j,t);
            t -= tmp;
            j++;
            vec.push_back(cir[i-1]*tmp);
        }
        if(cir[i] == -1){
            break;
        }
    }

    bitset<maxn> bs[2];
    int cur = 0, pre = 1;
    bs[cur][0] = 1;
    for(int i=0;i<vec.size();i++){
        swap(cur,pre);
        bs[cur] = bs[pre] | (bs[pre]<<vec[i]);
    }
    int MIN = k + 1 - bs[cur][k];

    cout<<MIN<<" "<<MAX<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值