AtCoder Beginner Contest 293(A-G)

D - Tying Rope (atcoder.jp)

        1.题目大意

                有n条绳子,一端是红色一端是蓝色,现在有n个操作,每一个操作把第x条绳子的一端连接第y条绳子的一端,问你最后成环的连通块和不成环的联通块各有多少。

         2.解题思路

                碰到环类问题,首先考虑并查集,对于每一个操作实际上就是把x和y合并,如果x和y已经合并说明该连通块是个环,则直接塞入一个set中记录即可,最后成环的块就是set的大小,不成环的块再遍历一次每一个连通块即可。

         3.代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int a[N];
struct UFS {
    int f[N],siz[N];
    void init(int n) {for(int i=1;i<=n;i++)siz[i]=1,f[i]=i;}
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    bool same(int x,int y){return find(x)==find(y);}
    void merge(int x,int y) {if(!same(x,y))siz[find(y)]+=siz[find(x)],f[find(x)]=find(y);}
    int qsz(int x){return siz[find(x)];}
};
void solve()
{
    int n,m;
    cin >> n >> m;
    UFS ufs;
    ufs.init(n);
    set <int> cycle;
    int ans = 0;
    rep(i,1,m) {
        int A,B;
        char op1,op2;
        cin >> A  >> op1 >> B >> op2;
        if(ufs.same(A,B)) {
            ans ++;
            cycle.insert(ufs.find(A));
        }
        else ufs.merge(A,B);
    }
    set <int> has;
    rep(i,1,n) {
        if(!cycle.count(ufs.find(i))) {
            has.insert(ufs.find(i));
        }
    }
    cout << ans << ' ' << sz(has) << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

E - Geometric Progression (atcoder.jp)

        1.题目大意

                问你求[0,x-1]的A^i的和模M是多少

         2.解题思路

                首先我们肯定考虑等比数列求和,但是你会发现需要用到除法,又因为题目不保证M为质数,因此除法不一定有逆元,想办法摆脱除法的限制。我们手画可以发现对于(A^0+A^1)*(A^2) = (A^2+A^3)因此提醒我们这题可以用分治思路加快速幂解决。

                对于指数b为偶数情况我们只需要单独算ksm(a,b,p)+继续分治的答案

                对于指数b为奇数情况我们只需要把分治的答案+分治的答案*(A^((b>>1)+1))

        3.代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
ll ksm(ll a,ll b,ll p)
{
    a %= p;
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
ll pow(ll a,ll b,ll p)
{
    if(!b) return 1;
    if(b & 1) return (ksm(a,(b >> 1) + 1,p) + 1) * pow(a,b >> 1,p) % p;
    else return (pow(a,b - 1,p) + ksm(a,b,p)) % p;
}
void solve()
{
    ll A,X,M;
    cin >> A >> X >> M;
    if(M == 1) cout << 0 << endl;
    else cout << pow(A,X - 1,M);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

F - Zero or One (atcoder.jp)         

        1.题目大意

                给你一个数n,问你有多少个进制能使得n在该进制下满足所有位数上都小于等于1

         2.解题思路

                首先考虑大于n是绝对不行的,然后再考虑1e18最多能有1000^6次方,因此对于n小于等于1000的情况下直接暴力枚举前1000的数对答案的贡献,剩下6次我们可以状压枚举每一位的状态,然后二分找到能不能有一个进制b在该bitmask下==n。

        3.代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int a[N];
ll n;
bool check(ll b)
{
    ll v = n;
    while(v) {
        if(v % b > 1) return false;
        v /= b;
    }
    return true;
}
//二分v进制
__int128 calc(int bit,ll v)
{
    __int128 s = 0,pw = 1;
    for(int j = 0;j < 7;j ++) {
        if(bit >> j & 1) {
            if(pw > n) return 9e18;
            else s += pw;
        }
        if(s > n) return 9e18;
        if(pw < 2e18) pw *= v;
    }
    return s;
}
bool binarySearch(int bit)
{
    __int128 l = 1002,r = 2e18;
    while(l <= r) {
        __int128 mid = (l + r) >> 1;
        if(calc(bit,mid) <= n) l = mid + 1;
        else r = mid - 1;
    }
    // if(calc(bit,l - 1) == n) cout << (bit) << ',' << (l - 1) << endl;
    return calc(bit,l - 1) == n;
}
void solve()
{
    cin >> n;
    //[1,1000] 暴力查找
    ll ans = 0;
    rep(i,2,min(n,1000*1ll)) ans += check(i);
    if(n > 1000) {
        //二分判断看是否存在进制有这个位置
        for(int j = 1;j < (1 << 7);j ++) {
            int bit = j;
            ans += binarySearch(bit);
        }
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    cin >> T;
    while(T --) solve();
    return 0;
}

G - Triple Index (atcoder.jp)

        1.题目大意

               给你一个长度为N的序列,然后有Q个询问,问你区间[l,r]有多少组ii,j,k满足a[i] = a[j] = a[k]。

          2.解题思路

                对于答案的贡献我们实际上就是某个数的中选三个也就是cnt[i] * (cnt[i] - 1) * (cnt[i] - 2) / 6,因此我们只需要统计区间[l,r]每个数的个数,又因为题目允许离线答案,因此考虑莫队算法,将查询分块进行答案计算,最后输出答案就可以。

          3.代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int a[N],belong[N],cnt[N];
ll ans[N];
struct Node {
    int l,r,id;
}Q[N];
int siz,block;
ll tot = 0;
ll calc(ll v)
{
    if(v < 3) return 0;
    return v * (v - 1) * (v - 2) / 3 / 2;
}
void solve()
{
    int n,q;
    cin >> n >> q;
    siz = sqrt(n);
    rep(i,1,n) cin >> a[i];
    for(int i = 1;i <= n;i++) belong[i] = (i - 1) / siz;
    rep(i,1,q) {
        cin >> Q[i].l >> Q[i].r;
        Q[i].id = i;
    }
    sort(Q + 1,Q + 1 + q,[](Node &c,Node &d){
        return (belong[c.l] ^ belong[d.l]) ? (belong[c.l] < belong[d.l]) : 
        ((belong[c.l] & 1) ? c.r < d.r : c.r > d.r);
    });
    // rep(i,1,q) cout << Q[i].l << ',' << Q[i].r << endl;
    int l = 1,r = 0;
    for(int i = 1;i <= q;i++) {
        int ql = Q[i].l,qr = Q[i].r;
        // cout << ql << ',' << qr << ',' << tot << endl;
        while(l < ql) {
            ll rev = cnt[a[l ++]];
            tot -= calc(rev);
            --cnt[a[l - 1]];
            rev --;
            tot += calc(rev);
        }
        while(l > ql) {
            ll rev = cnt[a[--l]] ++; 
            tot -= calc(rev);
            rev ++;
            tot += calc(rev);
        }
        while(r < qr) {
            ll rev = cnt[a[++r]]++; 
            tot -= calc(rev);
            rev ++;
            tot += calc(rev);
        }
        while(r > qr) {
            ll rev = cnt[a[r--]];
            tot -= calc(rev);
            -- cnt[a[r + 1]];
            rev --;
            tot += calc(rev);
        }
        ans[Q[i].id] = tot;
    }
    rep(i,1,q) cout << ans[i] << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值