A
假设 $a \geq b$
$$ \begin{aligned} \gcd(q^a-q,q^b-1) &= \gcd(q^b-1,q^a-q^b)\\ &= \gcd(q^b-1,q^b(q^{a-b}-1))\\ &= \gcd(q^b-1,q^{a-b}-1) \end{aligned} $$
使用取模加速运算即可。本质是对指数辗转相除。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
int q,a,b,ha;
inline int qpow(int a,int n=ha-2){
int res = 1;
while(n){
if(n & 1) res = 1ll*res*a%ha;
a = 1ll*a*a%ha;
n >>= 1;
}
return res;
}
inline int gcd(int a,int b){
if(a < b) return gcd(b,a);
if(!b) return (qpow(q,a)+ha-1)%ha;
return gcd(b,a%b);
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&q,&a,&b,&ha);
LL x = qpow(q,a)-1,y = qpow(q,b)-1;
printf("%d\n",gcd(a,b));
}
return 0;
}
B
开 1e6 个 vector 存一下所有的倍数,每次取出最少的,如果数量变成 $0$ 了就弹掉。
复杂度 $O(n \log n)$。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 1e6 + 5;
inline char nc(){
#define SIZE 1000000+3
static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
if(p1 == p2){
p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
if(p1 == p2) return -1;
}
return *p1++;
#undef SIZE
}
template <typename T>
inline void read(T &x){
x = 0;int flag = 0;char ch = nc();
while(!isdigit(ch)){
if(ch == '-') flag = 1;
ch = nc();
}
while(isdigit(ch)){
x = (x<<1) + (x<<3) + (ch^'0');
ch = nc();
}
if(flag) x = -x;
}
bool p[MAXN];
int prime[MAXN],tot,d[MAXN];
inline void prework(){
FOR(i,2,MAXN-1){
if(!p[i]) prime[++tot] = i,d[i] = i;
for(int j = 1;j <= tot && 1ll*i*prime[j] < MAXN;++j){
p[i*prime[j]] = 1;d[i*prime[j]] = prime[j];
if(!(i%prime[j])){
d[i*prime[j]] = d[i];
break;
}
}
}
}
int a[MAXN],n,m,cnt[MAXN];
std::vector<int> dec[MAXN];
int pp[MAXN],ee[MAXN],sz;
inline void dfs(int dep,int id,int sm=1){
if(dep == sz+1){
dec[id].pb(sm);
return;
}
dfs(dep+1,id,sm);
FOR(i,1,ee[dep]) sm *= pp[dep],dfs(dep+1,id,sm);
}
inline void fj(int i){
if(i == 1){
dec[1].pb(1);
return;
}
int x = i,las = d[x];x /= d[x];
sz = 0;int cnt = 1;
while(x != 1){
if(las != d[x]){
++sz;
pp[sz] = las;
ee[sz] = cnt;
las = d[x];cnt = 0;
}
x /= d[x];cnt++;
}
++sz;pp[sz] = las;ee[sz] = cnt;
dfs(1,i);
}
std::vector<int> G[MAXN];
int main(){
prework();
read(n);
FOR(i,1,n) read(a[i]),++cnt[a[i]];
ROF(i,MAXN-1,1){
if(cnt[i]){
fj(i);
for(auto x:dec[i]) G[x].pb(i);
}
}
read(m);std::vector<int> ans;
FOR(i,1,m){
int x;read(x);
while(!G[x].empty() && !cnt[G[x].back()]) G[x].pop_back();
if(G[x].empty()) break;
int v = G[x].back();
ans.pb(v);cnt[v]--;
}
printf("%d\n",(int)ans.size());
for(auto x:ans) printf("%d ",x);puts("");
return 0;
}
C
拆式子:
$$ \begin{aligned} &\sum_{i=1}^n \sum_{j=i+1}^n A_i^2B_j^2+A_j^2B_i^2-A_iA_jB_iB_j\\ &= (\sum_{i=1}^n A_i^2)(\sum_{j=1}^n B_j^2) - (\sum_{i=1}^n A_iB_i)^2\\ \end{aligned} $$
树状数组分开维护就好了。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 5e5 + 5;
const int ha = 998244353;
const int inv2 = 499122177;
inline void add(int &x,int y){
x += y;if(x >= ha) x -= ha;
}
int a[MAXN],b[MAXN];
inline char nc(){
#define SIZE 1000000+3
static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
if(p1 == p2){
p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
if(p1 == p2) return -1;
}
return *p1++;
#undef SIZE
}
template <typename T>
inline void read(T &x){
x = 0;int flag = 0;char ch = nc();
while(!isdigit(ch)){
if(ch == '-') flag = 1;
ch = nc();
}
while(isdigit(ch)){
x = (x<<1) + (x<<3) + (ch^'0');
ch = nc();
}
if(flag) x = -x;
}
struct BIT{
#define lowbit(x) ((x)&(-(x)))
int tree[MAXN];
inline void add(int pos,int x){
while(pos < MAXN){
::add(tree[pos],x);
pos += lowbit(pos);
}
}
inline int calc(int pos){
if(!pos) return 0;
int res = 0;
while(pos){
::add(res,tree[pos]);
pos -= lowbit(pos);
}
return res;
}
inline int query(int l,int r){
return (calc(r)+ha-calc(l-1))%ha;
}
}bit1,bit2,bit3;
int n,q;
int main(){
read(n);read(q);
FOR(i,1,n) read(a[i]),bit1.add(i,1ll*a[i]*a[i]%ha);
FOR(i,1,n) read(b[i]),bit2.add(i,1ll*b[i]*b[i]%ha);
FOR(i,1,n) bit3.add(i,1ll*a[i]*b[i]%ha);
FOR(i,1,q){
int opt;read(opt);
if(opt == 1){
int l,r;read(l);read(r);
int a = bit1.query(l,r),b = bit2.query(l,r),c = bit3.query(l,r);
int res = (1ll*a*b%ha+ha-1ll*c*c%ha)%ha;
printf("%d\n",res);
}
else{
int p;read(p);
bit1.add(p,ha-1ll*a[p]*a[p]%ha);
bit2.add(p,ha-1ll*b[p]*b[p]%ha);
bit3.add(p,ha-1ll*a[p]*b[p]%ha);
read(a[p]);read(b[p]);
bit1.add(p,1ll*a[p]*a[p]%ha);
bit2.add(p,1ll*b[p]*b[p]%ha);
bit3.add(p,1ll*a[p]*b[p]%ha);
}
}
return 0;
}
D
这个题循环是假的,实际是给定了一些不等式关系,要求你确定有多少组解。
我们只关心相对大小关系;如果相对大小关系不同方案绝对不同,相对大小关系也决定了有多少组数是相等的。
所以设 $f_{i,S}$ 表示从小到大选出了 $i$ 组,已经用了 $S$ 内的数的方案数,每次枚举一个子集,要满足 $S$ 内的数都不能大于这个子集内的数,转移即可。
然后就可以用组合数计算答案了。这里注意到 $m$ 很小,可以手动约分。(居然之前没注意过)
加强
复杂度可以做到 $O(n^2 \cdot 2^n)$。
做法是我们首先缩点,发现一个 SCC 内的点一定同时选,于是图变成了 DAG ,选择方案合法当且仅当当前选出的集合没有指向之前集合的边。。所以还要设 $f_{i,j,S}$ 表示考虑到第 $i$ 个点,用了 $j$ 个集合,选择了 $S$ 内的点,然后转移枚举这个是否要新建集合。。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 15 + 1;
int n,m,ha;
inline void add(int &x,int y){
x += y;if(x >= ha) x -= ha;
}
// a < b : a -> b
int small[(1<<MAXN)+3],big[(1<<MAXN)+3];
int f[MAXN][(1<<MAXN)+3];
// 选出了 S 内 i 组,组内相同的方案数
// 每次从小到大划分组
int tmp[MAXN];
inline int C(int n,int m){
if(n < m) return 0;
FOR(i,1,m) tmp[i] = n-i+1;
FOR(i,2,m){
int x = i;
FOR(j,1,m){
int g = std::__gcd(tmp[j],x);
x /= g;tmp[j] /= g;
}
}
int ans = 1;
FOR(i,1,m) ans = 1ll*ans*tmp[i]%ha;
return ans;
}
int main(){
scanf("%d%d%d",&m,&n,&ha);//ha = 998244353;
FOR(i,0,m-1){
int k;scanf("%d",&k);
while(k--){
int x;scanf("%d",&x);--x;
small[1<<i] |= (1<<x);
big[1<<x] |= (1<<i);
}
scanf("%d",&k);
while(k--){
int x;scanf("%d",&x);--x;
small[1<<x] |= (1<<i);
big[1<<i] |= (1<<x);
}
}
FOR(S,0,(1<<m)-1){
FOR(i,0,m-1){
if((S>>i)&1){
small[S] |= small[S^(1<<i)];
big[S] |= big[S^(1<<i)];
}
}
}
f[0][0] = 1;
FOR(i,0,m-1){
FOR(S,0,(1<<m)-1){
if(!f[i][S]) continue;
int s = ((1<<m)-1)^S;
for(int T = s;T;T = (T-1)&s){
if(((big[T]&S) == 0)){
add(f[i+1][S^T],f[i][S]);
}
}
}
}
int ans = 0;
FOR(i,1,m) (ans += 1ll*C(n,i)*f[i][(1<<m)-1]%ha) %= ha;
printf("%d\n",ans);
return 0;
}