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;
}
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;
}
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;
}