11.10总结

11.10 GZEZ NOIP2022模拟测试赛(五十八)

Problem A :

题面描述 :

\(T\)组询问,每组询问给定\(n,m,a\)

\(\sum \limits_{i = 1}^{n} \sum \limits_{j=1}^{m}lcm\left(i,j\right)\left[gcd\left(i,j\right)\le a\right]\)

Solution:

别人:好,一眼切了

我:我眼呢,💢💢

这是\(Crash\)加强版,来推狮子:

\[\text{先枚举gcd } \sum \limits_{k=1}^{a} \sum \limits_{i=1}^{n}\sum \limits_{j=1}^{m}\frac{i*j}{k} \left[gcd\left(i,j\right)==1\right]\ 莫反一下\sum \limits_{k=1}^{a} \sum \limits_{i=1}^{n}\sum \limits_{j=1}^{m}\frac{i*j}{k}\sum\limits_{d\mid gcd(i,j)}\mu(d)\\ 再划一下把k乘过来\sum \limits_{k=1}^{a} \sum \limits_{i=1}^{[\frac{n}{k}]} \sum \limits_{j=1}^{[\frac{m}{k}]} {i*j*k} \sum\limits_{d\mid gcd(i,j)}\mu(d)\\ 把d\mid gcd拆开\sum \limits_{k=1}^{min(n,m)} \sum \limits_{i=1}^{[\frac{n}{k}]}\sum \limits_{j=1}^{[\frac{m}{k}]}{i*j*k}\sum\limits_{d\mid i,d\mid j}\mu(d)\\ 提到前面去\sum \limits_{k=1}^{a}k\sum\limits_{d=1}^{min(n,m)}\mu(d)\sum \limits_{i=1}^{[\frac{n}{k}]} i[d\mid i]\sum \limits_{j=1}^{[\frac{m}{k}]}{j[d\mid j]}\\ 把d除过去\sum \limits_{k=1}^{a}k\sum\limits_{d=1}^{min(n,m)}\mu(d)*d^2\sum \limits_{i=1}^{[\frac{n}{kd}]} {i}\sum \limits_{j=1}^{[\frac{m}{kd}]}{j}\\ \]

现在解决了\(T=1\)的情况,考虑\(T=10000\)

\[T = kd\\ \sum \limits_{T=1}^{n} \sum \limits_{k=1}^{a} \mu(\frac{T}{k})*\frac{T^2}{k}\sum\limits_{i=1}^{[\frac{n}{kd}]}i\sum\limits_{j=1}^{[\frac{m}{kd}]}j\\ 后面两个求$i,j$可以O(1)算,求k后面数论分块根号的 \]

考虑整数分块。 对于每⼀段\([\frac{n}{p}],[\frac{m}{p}]\) , 相同的分⼀块。 ⼀共只有\(\sqrt{n}\)块。 将询问按\(a\)排序。考虑在树状数组上放\(a\)的下标然后当你\(a\)\(a+i\)的贡献去枚举d的倍数然后在树状数组上单点加

Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int SIZE = 1e5 + 10;
const int mod = 1e9 + 7;

int T, cnt;
int prime[SIZE], mo[SIZE], use[SIZE];
long long ans[SIZE];
struct node
{
	int n, m, a ;
	int id;
} q[SIZE];
inline bool cmp(node x ,node y){
	return x.a < y.a ;
}
inline
namespace BIT
{
	long long tree[SIZE];
	#define lowbit(x) (x & -x)
	int Limm = 1e5 ;
	inline void add(int x, int k){
		while(x <= Limm){
			tree[x] += k ;
			x += lowbit(x) ;
		}
	}
	inline ll query(int x){
		ll ans = 0;
		while(x){
			ans += tree[x] ;
			x -= lowbit(x) ;
		}
		return (ans + mod) % mod;
	}
	inline ll query(int l, int r){
		return (query(r) - query(l - 1) + mod) % mod;
	}
	inline void Add(int d)
	{
		for (ll T = d; T <= 1e5; T += d){
			add(T, (ll)T * (T / d) % mod * mo[T / d]);
		}
	}
};
using namespace BIT ;


inline void init(int n){
	mo[1] = 1 ;
	for(int i = 2 ; i <= n ; i += 1){
		if(!use[i]) prime[++cnt] = i , mo[i] = -1 ;
		for(int j = 1 ; j <= cnt && i * prime[j] <= n ; j += 1){
			use[i * prime[j]] = 1 ;
			if(i % prime[j] == 0) break ;
			mo[i * prime[j]] = -mo[i] ;
		}
	}
}
inline ll work(int x){
	return x * (x + 1ll) / 2 % mod ;
}
inline ll calc(int n , int m){
	int lim = min(n , m) ;
	ll ans = 0 ;
	for(int l = 1 , r ; l <= lim ; l = r + 1){
		r = min(n / (n / l) , m / (m / l)) ;
		ans += work(n / l) * work(m / l) % mod * query(l , r) % mod ;
	}
	return (ans % mod + mod) % mod ;
}


int main()
{
	cin >> T ;
	init(1e5);
	for (int i = 1; i <= T; i += 1)
	{
		cin >> q[i].n >> q[i].m >> q[i].a ;
		q[i].id = i;
	}
	sort(q + 1, q + T + 1, cmp) ;
	for (int x = 1, i = 0; x <= 1e5 ; x += 1)
	{
		Add(x);
		while (i < T and q[i + 1].a == x){
			i += 1 ;
			ans[q[i].id] = calc(q[i].n, q[i].m) ;
		}
			
	}
	for (int i = 1; i <= T; i += 1){
		cout << ans[i] << '\n' ;
	}
}

Problem B :1

题面描述 :

给你一个连通的平面图(平面图是可以画在平面上并且使得不同的边可以互不交叠的图)。接下来有 \(q\) 次操作:

  • 操作"-", 删去边 $x, y $, 询问删完后有几个连通块。
  • 操作"?", 询问 $ x, y $ 在不在一个连通块中, 如果是, 输出 \(1\) , 否则输出 \(0\)
Solution:

因为没有 操作,所以只需要求出有哪些被删掉的边是关建边。 因为平⾯图中的⼀个割对应对偶图中的⼀个环,先求出对偶图,在⽤并查集维护对偶图上的连通性。

现在已经知道有哪些边是关键边了,需要动态维护所在连通块。 隔断⼀条关键边的时候考虑启发式分裂,给分裂出来的较小的连通块重新标号。

Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long    
#define cout io
#define cin io
#define endl '\n'    
#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,100000,stdin),p1==p2)?(EOF):(*p1++))
#define putchar(x) ((p3==obuf+100000)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
#endif
char ibuf[100000],obuf[100000];char*p1=ibuf,*p2=ibuf,*p3=obuf;const ll pow10[]={(ll)1e0,(ll)1e1,(ll)1e2,(ll)1e3,(ll)1e4,(ll)1e5,(ll)1e6,(ll)1e7,(ll)1e8,(ll)1e9,(ll)1e10,(ll)1e11,(ll)1e12,(ll)1e13,(ll)1e14,(ll)1e15,(ll)1e16,(ll)1e17,(ll)1e18,};struct Octane_t{~Octane_t(){fwrite(obuf,p3-obuf,1,stdout);}bool flag=false;operator bool(){return flag;}}io;template<typename T>inline T read(){T s=0;int w=1;char ch;while(ch=getchar(),!isdigit(ch)&&(ch!=EOF))if(ch=='-')w=-1;if(ch==EOF)return 0;while(isdigit(ch))s=s*10+ch-48,ch=getchar();return s*=w;}template<typename T>inline bool read(T&s){s=0;int w=1;char ch;while(ch=getchar(),!isdigit(ch)&&(ch!=EOF))if(ch=='-')w=-1;if(ch==EOF)return false;while(isdigit(ch))s=s*10+ch-48,ch=getchar();return s*=w,true;}inline bool read(char&s){while(s=getchar(),isspace(s));return s!=EOF;}inline bool read(char*s){char ch;while(ch=getchar(),isspace(ch));if(ch==EOF)return false;while((ch>32))*s++=ch,ch=getchar();*s='\000';return true;}template<typename T>void print(T x){static int t[20];int top=0;if(x<0)putchar('-'),x=-x;do{t[++top]=x%10;x/=10;}while(x);while(top)putchar(t[top--]+48);}struct empty_type{};int pcs=8;empty_type setpcs(int cnt){return pcs=cnt,empty_type();}inline void print(empty_type x){}inline void print(char x){putchar(x);}inline void print(char*x){for(int i=0;x[i];i++)putchar(x[i]);}inline void print(const char*x){for(int i=0;x[i];i++)putchar(x[i]);}template<typename T>Octane_t&operator>>(Octane_t&io,T&b){return io.flag=read(b),io;}Octane_t&operator>>(Octane_t&io,char*b){return io.flag=read(b),io;}template<typename T>Octane_t&operator<<(Octane_t&io,T b){return print(b),io;};
// code by pt 
const int N = 4e5+7;
int read()
{
	int x;
	cin >> x;
	return x;
}

#define pa pair<int,int>
#define fi first
#define se second
map<pa,int>mp; // 编号 
map<pa,int>id;// 边的左边的面 

// cnt 面的数量 
int n,q,cnt;
// belong 原图中点的染色 
int belong[N],num;
int head[N];bool vis[N];
vector<int>gra[N];
int f[N];
void init(int n){for(int i=1;i<=n;i++) f[i]=i;}
int find(int x){return f[x]==x ? x : f[x] = find(f[x]);}
void merge(int x,int y){
	if(find(x)==find(y)) return ;
	f[find(y)] = find(x);
}
bool check(int x,int y){return find(x)==find(y);}

void dfs(int x,int pre,int st)
{
	if(x==st) return ;
	int p = mp[{x,pre}];
	p = (p+1)%gra[x].size();
	int y = gra[x][p];
	id[{x,y}]=cnt;
	dfs(y,x,st);
}
int extend(vector<int> &v,int &st)
{
	for(;st<v.size();++st)
	{
		int x = v[st];
		for(int i=head[x];i<gra[x].size();head[x] = ++ i)
		{
			int y = gra[x][i];
			if(!vis[y])
			{
				vis[y] = true;
				v.emplace_back(y);
				return true;
			}
		}
	}
	return false;
}
signed main()
{
	n = read(); q= read();
	for(int x=1;x<=n;x++)
	{
		int k = read();
		for(int i=1;i<=k;i++)
		{
			int y;
			cin >> y;
			gra[x].emplace_back(y);
			mp[{x,y}]=i-1;
		}
	}
	
	for(int x=1;x<=n;x++)
	{
		for(auto y:gra[x])
		{
			if(id.count({x,y})) continue;
			id[{x,y}] = ++ cnt;
			dfs(y,x,x);
		}
	}
	for(int x=1;x<=n;x++) sort(gra[x].begin(),gra[x].end());
	init(cnt);
	char opt;int x;int y;
	int tot = 1;
	int lasans = 0;
	for(int i=1;i<=q;i++)
	{
		cin >> opt >> x >> y;
		x^=lasans;
		y^=lasans;
		if(opt=='-')
		{
			// qwq
			int a = id[{x,y}] , b= id[{y,x}];
			gra[x].erase(lower_bound(gra[x].begin(),gra[x].end(),y));
			gra[y].erase(lower_bound(gra[y].begin(),gra[y].end(),x));
			
			if(find(a)==find(b))
			{
				// 出现了环
				tot ++ ;
				int p1=0;int p2=0;
				vector<int>v1,v2;
				v1.emplace_back(x);
				v2.emplace_back(y);
				while(1)
				{
					if(!extend(v1,p1)){
						num ++ ;
						for(auto v:v1) belong[v]=num;
						break;
					}
					if(!extend(v2,p2)){
						num ++ ;
						for(auto v:v2) belong[v]=num;
						break;
					}
				}
				for(auto v:v1) head[v]=vis[v] = 0;
				for(auto v:v2) head[v]=vis[v]=0;
				 
			}
			else merge(a,b);
			cout << (lasans = tot) << '\n';
			
		}
		else cout << (lasans = (belong[x]==belong[y])) << '\n';
	}
	
	return 0;
	
} 

Problem C :

题面描述 :

你现在要从 \((0,0)\) 走到 \((n, m)\) , 只能向上或向右走。显然, 一个有 $ \frac{(n+m) !}{n ! m !}$ 种路径。
对于每一种路径, 假设它和两条直线 \(y=0, x=n\) 构成的封闭图形面积为 \(k\) , 那么这条路径的贡献为 $q^{k} $, 求所有路径的贡献和对 \(p\) 取模的结果

Solution:

考虑答案为 $ \frac{F(n+m)}{F(n) F(m)} $
其中 $F(n)=\prod\left(q^{i}-1\right) $
\(k\) 为 $ \min \left(k>0, q^{k}-1 \equiv 0 \bmod p\right) \( \) F(n)=\prod_{1 \leq i \leq \frac{n}{k}}\left(q^{i k}-1\right) \prod_{1 \leq i \leq n, k \nmid n}\left(q^{i}-1\right) $
后半部分可以快速预处理。
考虑 \(\prod_{1 \leq i \leq \frac{n}{k}}\left(q^{i k}-1\right) =\left(q^{k}-1\right)^{n / k} \prod_{i=1}^{n / k}\left(\sum_{j=0}^{i-1} q^{k j}\right) \)而 $\sum_{j=0}^{i-1} q^{k j} \equiv i \bmod p $
对于所有 \(p \mid i\) 的 $ i $

\(\begin{array}{l} \prod_{i=1}^{n / k p}\left(\sum_{j=0}^{p i-1} q^{k j}\right) =\left(\sum_{j=0}^{p-1} q^{k j}\right)^{n / p k} \prod_{i=1}^{n / k p}\left(\sum_{j=0}^{i-1} q^{j k p}\right) \end{array}\)

可以递归处理。

Code:
#include <bits/stdc++.h>
    
using namespace std;
    
typedef long long ll;
    
int MOD;
    
ll pow_mod(ll x,int k) {
  ll ans=1;
  while (k) {
    if (k&1) ans=ans*x%MOD;
    x=x*x%MOD;
    k>>=1;
  }
  return ans;
}
    
ll mid[200005],miv[200005],mi[200005];
ll facd[200005],facv[200005];
int k;
    
void pre(ll q) {
  ll v=1;
  do {
    v=v*q%MOD;
    k++;
  } while (v!=1);
  mi[0]=mid[0]=1;
  for(int i=1;i<k;i++) {
    mi[i]=mi[i-1]*q%MOD;
    mid[i]=mid[i-1]*(mi[i]-1LL+MOD)%MOD;
  }
  miv[k-1]=pow_mod(mid[k-1],MOD-2);
  for(int i=k-2;i>=0;i--) miv[i]=miv[i+1]*(mi[i+1]-1LL+MOD)%MOD;
  facd[0]=1;
  for(int i=1;i<MOD;i++) facd[i]=facd[i-1]*i%MOD;
  facv[MOD-1]=pow_mod(facd[MOD-1],MOD-2);
  for(int i=MOD-2;i>=0;i--) facv[i]=facv[i+1]*(i+1)%MOD;
}
    
ll calc1(int n,int m) {
  return mid[n]*miv[m]%MOD*miv[n-m]%MOD;
}
   
ll calc2(int n,int m) {
  return facd[n]*facv[m]%MOD*facv[n-m]%MOD;
}
    
ll solve(int n,int m) {
  if (!m) return 1;
  if (n%MOD<m%MOD) return 0;
  return solve(n/MOD,m/MOD)*calc2(n%MOD,m%MOD)%MOD;
}
    
int main() {
  int cases,q;
  scanf("%d%d%d",&cases,&q,&MOD);
  if (q==MOD) {
    for(;cases;cases--) puts("1");
    return 0;
  }
  pre(q);
  for(;cases;cases--) {
    int n,m;
    scanf("%d%d",&n,&m);
    if ((n+m)%k<n%k) puts("0");
    else printf("%lld\n",calc1((n+m)%k,n%k)*solve((n+m)/k,n/k)%MOD);
  }
  return 0;
}
posted @ 2022-11-10 20:36  Guier-Lime  阅读(19)  评论(0编辑  收藏  举报