杂题乱做

MY

T1

给定一个除源点和汇点外有 \(L\) 层,每层 \(N\) 个点的有向图,相邻层数之间都有连边,相邻层数之间边的权值是这条边指向的点的权值(每层编号相同的点权值相同),告诉你源点到第一层边的权值,每个点的权值,最后一层到汇点的权值,求源点到汇点的路径长度能被 \(m\) 整除的个数。

先考虑 brute force : 不难发现相邻层之间点的编号影响不了什么,设 \(dp_{i,j}\) 表示在第 \(i\) 层,路径长度为 \(j\) 的方案数。

不难得出转移:\(dp_{i,j}=\displaystyle\sum_{k=0}^{m} dp_{i-1,j-k} \times cnt_k\),这里 \(cnt_k\) 表示 \(k\) 在相邻两层之间的出现次数。

可是在最后一层时,边的权值与点的编号有关,这个方程就不对了。

我们把最后一层和汇点的边合并,然后分开来处理。

具体来说就是这样:

My Code
for(int i=1; i<=n; i++) {
        ed[(read()%m+val[i])%m]++;
}
/* dp */
for(int i=0; i<m; i++) {
	for(int k=0; k<m; k++) {
		f[l][(i+k)%m]+=f[l-1][i]*ed[k];
	}
}

发现 \(m\) so small, \(L\) so big 不难想到矩阵乘法。

我们观察一下这个 DP:

My Code
f[0][0]=1;
for(int i=2; i<l; i++) {
	for(int j=0; j<m; j++) {
		for(int k=0; k<m; k++) {
			f[i][(j+k)%m]+=f[i-1][j]*w[k];
		}
	}
}

它和矩阵乘法有着相同的性质(结合律之类的)。

这种类矩阵乘法可以这么写:

My Code
class matrix {
	public:
		int c[M];
		matrix(){memset(c,0,sizeof(c));}
		matrix operator *(const matrix &bb) const {
			matrix ret;
			for(int i=0; i<m; i++) {
				for(int j=0; j<m; j++) {
					ret.c[(i+j)%m]=(ret.c[(i+j)%m]+c[i]*bb.c[j]%Mod)%Mod;
				}
			}
			return ret;
		}
} fr,f,ed;

然后做快速幂就可以了。

T2

考虑先建 DFS 树。

对于树上的一个节点 \(u\),我们判断它子树内的选择情况是否能满足它的度数限制,如果可以就不管,如果不可以就将 \(fa_u\)\(u\) 的边选上。

这样最后不满足限制的就只可能有 \(rt\),解决方法就是选择一个限制为 -1 的节点 \(v\),将 \(v \to rt\) 上的节点全部取反。

如果没有就无解。

My Code
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+8;
inline int read() {
	int s=1,a=0;char c=getchar();
	while(!isdigit(c)) {if(c=='-') s=-s;c=getchar();}
	while(isdigit(c)) {a=(a<<3)+(a<<1)+(c^48),c=getchar();}
	return s*a;
}
int n,m;
struct graph {
	int fr,to,d,id;
} g[N];
struct edge {
	int nxt,to,id;
} e[N<<1];
class UFS {
	private:
		int fa[N];
	public:
		void init() {
			for(int i=1; i<=n; i++) fa[i]=i;
		}
		int find(int x) {
			return fa[x]==x?x:fa[x]=find(fa[x]);
		}
		void merge(int x,int y) {
			int fx=find(x),fy=find(y);
			if(fx==fy) return;
			fa[fy]=fx;
		}
} dsu;
int head[N],idx,fa[N],ch[N],w[N],deg[N],nd;
void add(int u,int v,int id) {
	e[++idx].nxt=head[u];
	e[idx].to=v;
	e[idx].id=id;
	head[u]=idx;
}
void build() {
	dsu.init();
	int cnt=0;
	for(int i=1; i<=m; i++) {
		int u=g[i].fr,v=g[i].to;
		int fx=dsu.find(u),fy=dsu.find(v);
		if(fx!=fy) {
			cnt++;
			dsu.merge(fx,fy);
			add(u,v,g[i].id);
			add(v,u,g[i].id);
		}
		if(cnt==n-1) break;
	}
}
int flg=0;
void dfs1(int u,int f) {
	if(deg[u]==-1) flg=1,nd=u;
	for(int i=head[u]; i; i=e[i].nxt) {
		int v=e[i].to;
		if(v==f) continue;
		fa[v]=u;
		w[v]=e[i].id;
		dfs1(v,u);
	}
}
void dfs2(int u,int f) {
	int cnt=0;
	for(int i=head[u]; i; i=e[i].nxt) {
		int v=e[i].to;
		if(v==f) continue;
		dfs2(v,u);
		if(ch[e[i].id]) cnt++;
	}
	if(deg[u]==-1||(cnt&1)==deg[u]) return;
	else ch[w[u]]=1;
}
bool check() {
	int cnt=0;
	for(int i=head[1]; i; i=e[i].nxt) {
		int v=e[i].to;
		if(ch[e[i].id]) cnt++;
	}
	// cout<<cnt<<" "<<deg[1]<<endl;
	if(deg[1]==-1||(cnt&1)==deg[1]) return true;
	return false;
}
void rework() {
	int u=nd;
	while(u!=1) {
		ch[w[u]]^=1;
		u=fa[u];
	}
}
vector <int> ans;
void getans(int u) {
	for(int i=head[u]; i; i=e[i].nxt) {
		int v=e[i].to;
		if(v==fa[u]) continue;
		if(ch[e[i].id]) ans.push_back(e[i].id);
		getans(v);
	}
}
int main() {
	n=read(),m=read();
	for(int i=1; i<=n; i++) {
		deg[i]=read();
	}
	for(int i=1; i<=m; i++) {
		int u=read(),v=read();
		g[i].fr=u,g[i].to=v;
		g[i].id=i;
	}
	build();
	dfs1(1,0);
	dfs2(1,0);
	if(!check()) {
		// cout<<1<<endl;
		if(!flg) puts("-1"),exit(0);
		else rework();
	}
	getans(1);
	sort(ans.begin(),ans.end());
	printf("%d\n",ans.size());
	for(auto v:ans) {
		printf("%d\n",v);
	}
	return 0;
}

T3

  • 对于 Task1

我们肯定可以发现 \(n\) 一定要放在最前面。

否则假设 \(n\) 在位置 \(i\),则 \(sum_{i-1} \equiv sum_{i} \pmod{n}\)

然后就可以得出长度一定为偶数(\(1\) 除外),如果为 \(1\) 以外的奇数,则:\(\displaystyle\sum_{i=2}^{n}a_i=\displaystyle\sum_{i=1}^{n-1}=n\times \frac{n-1}{2} \equiv 0 \pmod{n}\)

如果是偶数 \(n\) 会被 \(2\) 约掉。

否则,我们考虑构造一个这样的序列 \(0,-1,2,-3,\cdots ,-(n-1)\)

在模意义下,这就是 \(n\) 的排列。

  • 对于 Task2

考虑当 \(n\) 为合数时,那么一定有两个数 \(x,y\) 使得 \(xy \equiv 0 \pmod{n}\)

当然 \(4\) 是个特例特例,就是敌人

\(n\) 为质数是,一定可以构造一个这样的序列 \(1,\displaystyle\frac{2}{1},\frac{3}{2},\cdots,\frac{n-1}{n-2},\frac{n}{n-1}\)

在模意义下,这就是 \(n\) 的排列。

My Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+8;
inline int read() {
	int s=1,a=0;char c=getchar();
	while(!isdigit(c)) s=(c=='-')?(-s):s,c=getchar();
	while(isdigit(c)) a=(a<<3)+(a<<1)+(c^48),c=getchar();
	return s*a;
}
class subtask1 {
	private:
		int a[N],n;
	public:
		void solve() {
			n=read();
			if((n&1)&&(n^1)) puts("0");
			else {
				printf("2 ");
				if(n==1) printf("1");
				else {
					for(int i=1; i<=n; i++) {
						printf("%d ",(i&1)?(n-i+1):(i-1));
					}
				}
				puts("");
			}
		}
} sol1;
class subtask2 {
	#define int long long
	private:
		int a[N],n;
		int notprime[N],prime[N],cnt;
		int ksm(int a,int b) {
			int c=1;
			while(b) {
				if(b&1) c*=a,c%=n;
				a*=a,a%=n;
				b>>=1;
			}
			return c;
		}
	public:
		void init() {
			notprime[0]=notprime[1]=1;
			for(int i=2; i<N; i++) {
				if(!notprime[i]) {
					prime[++cnt]=i;
				}
				for(int j=1; j<=cnt&&i*prime[j]<N; j++) {
					if(i%prime[j]==0) {
						notprime[i*prime[j]]=1;
						break;
					}
					notprime[i*prime[j]]=1;
					// cout<<i*prime[j]<<" "<<notprime[i*prime[j]]<<endl;
				}
			}
		}
		void solve() {
			n=read();
			if(notprime[n]&&n!=1&&n!=4) puts("0");
			else {
				printf("2 ");
				if(n==1) printf("1");
				else if(n==4) printf("1 3 2 4");
				else {
					printf("1 ");
					for(int i=2; i<n; i++) {
						printf("%d ",ksm(i-1,n-2)*i%n);
					}
					printf("%d",n);
				}
				puts("");
			}
		}
	#undef int
} sol2;
int main() {
	int X=read(),T=read();
	if(X==2) sol2.init();
	while(T--) {
		if(X==1) sol1.solve();
		else sol2.solve();
	}
	return 0;
}

T4

考虑当 \(L=2^k\) 时,我们可以用如下方式构造:建 \(k+1\) 个点,\(i \to i+1 \ with \ 0\)\(1 \to i+1 \ with \ 2^i\)

考虑更一般的情况,先找到 \(2^k \le L\) 最大的 \(k\),对于 \(L\) 的每一位 \(i\),如果这一位为 \(1\),我们就向 \(k+1\) 连一条边权为 \(L\) 抹去 \(i\) 及其以下的位的边。

这样 \([0,L-1]\) 之间的每一种边权都出现了一次。

点数最多为 \(\lfloor \log L \rfloor +1\),边数最多为 \(3\lfloor \log L \rfloor\) 条,满足条件。

My Code
#include<bits/stdc++.h>
using namespace std;
int L,w,res;
inline int read() {
	int s=1,a=0;char c=getchar();
	while(!isdigit(c)) s=(c=='-')?(-s):s,c=getchar();
	while(isdigit(c)) a=(a<<3)+(a<<1)+(c^48),c=getchar();
	return s*a;
}
struct edge {
	int fr,to,dis;
};
vector <edge> ans;
int main() {
	L=read();
	int f=L;
	while(f) {
		w++;
		f>>=1;
	}
	for(int i=1; i<w; i++) {
		ans.push_back((edge){i,i+1,0});
		ans.push_back((edge){i,i+1,(1<<(i-1))});
	}
	for(int i=0; i<w-1; i++) {
		if((L>>i)&1) {
			ans.push_back((edge){i+1,w,L&(~((1<<(i+1))-1))});
		}
	}
	printf("%d %d\n",w,ans.size());
	for(auto v:ans) {
		printf("%d %d %d\n",v.fr,v.to,v.dis);
	}
	return 0;
}

T5

考虑只有一个 \(D\) 时怎么做。

我们将距离恰好为 \(D\) 的点连边,可以证明它一定是二分图。

证明:

设有两个点 \((x_1,y_1)\)\((x_2,y_2)\) 的距离为 \(D\)

\((x_1-x_2)^2+(y_1-y_2)^2=D\)

我们可以发现 \((x_1-x_2)^2\) 的奇偶性和 \((x_1-x_2)\) 的奇偶性是一样的。

所以 \((x_1-x_2)^2+(y_1-y_2)^2\) 的奇偶性和 \(x_1+y_1-(x_2+y_2)\) 的奇偶性是一样的。

  • \(D\) 为奇数时,\(x_1+y_1\)\(x_2+y_2\) 的奇偶性显然不一样,所以一定是二分图。

  • \(D\) 为偶数时,奇偶性是一样的,就不好判断。

但我们还是可以判断出来。

以下默认 \(D\) 为偶数。

  • \(D\equiv 2 \pmod{4}\) 时,\(x_1-x_2\)\(y_1-y_2\) 一定是奇数,即 \(x_1,x_2\)\(y_1,y_2\) 的奇偶性一定不同,所以一定是形如(奇,奇)连(偶,偶),也是二分图。

  • \(4|D\) 时,我们可以把原式转成 \(4(x_1^\prime-x_2^\prime)^2+4(y_1^\prime-y_2^\prime)^2 = D\) 的形式,然后两边约 \(4\),最终一定可以转成上一种情况。

证毕。

对其黑白染色,发现同种颜色一定可以一起选。

对于有两个 \(D\),建两个图,我们将原点集划分成四个不相交的集合:

  • 在第一幅图中为黑,在第二幅图中为黑。
  • 在第一幅图中为白,在第二幅图中为黑。
  • 在第一幅图中为黑,在第二幅图中为白。
  • 在第一幅图中为白,在第二幅图中为白。

根据鸽巢原理,一定有一个大于 \(O(n^2)\)

My Code
#include<bits/stdc++.h>
using namespace std;
inline int read() {
	int s=1,a=0;char c=getchar();
	while(!isdigit(c)) {if(c=='-') s=-s;c=getchar();}
	while(isdigit(c)) {a=(a<<3)+(a<<1)+(c^48),c=getchar();}
	return s*a;
}
const int N=700;
int n,d1,d2;
int getpos(int x,int y) {
	return (x-1)*2*n+y;
}
inline bool in(int x,int y) {
	return (x>=1)&&(x<=n*2)&&(y>=1)&&(y<=n*2);
}
class Graph {
	private:
		vector <int> G[N*N];
		int vis[N*N],col[N*N];
	public:
		int dd;
		Graph(){memset(col,0,sizeof(col)),memset(vis,0,sizeof(vis));}
		void add(int u,int v) {
			G[u].push_back(v);
			G[v].push_back(u);
		}
		void make_edge() {
			for(int i=1; i<=2*n; i++) {
				for(int j=1; j<=2*n; j++) {
					for(int x=0; x*x<=dd; x++) {
						int y=sqrt(dd-x*x);
						if(x*x+y*y==dd) {
							if(in(i+x,j+y)) add(getpos(i,j),getpos(i+x,j+y));
							if(in(i-x,j+y)) add(getpos(i,j),getpos(i-x,j+y));
							if(in(i+x,j-y)) add(getpos(i,j),getpos(i+x,j-y));
							if(in(i-x,j-y)) add(getpos(i,j),getpos(i-x,j-y));
						}
					}
				}
			}
		}
		void dfs(int u,int w) {
			vis[u]=1;
			col[u]=w;
			for(auto v:G[u]) {
				if(!vis[v]) {
					dfs(v,w^1);
				}
			}
		}
		void solve() {
			for(int i=1; i<=4*n*n; i++) {
				if(!vis[i]) dfs(i,0);
			}
		}
		int c(int v) {
			return col[v];
		}
} g1,g2;
vector <pair<int,int>> S[4];
vector <pair<int,int>> work(int c1,int c2) {
	vector <pair<int,int>> ret;
	ret.clear();
	for(int i=1; i<=2*n; i++) {
		for(int j=1; j<=2*n; j++) {
			if(g1.c(getpos(i,j))==c1&&g2.c(getpos(i,j))==c2) {
				ret.push_back(make_pair(i,j));
				// cout<<i<<" "<<j<<" "<<c1<<" "<<c2<<endl;
			}
		}
	}
	return ret;
}
int main() {
	n=read(),g1.dd=read(),g2.dd=read();
	g1.make_edge();
	g1.solve();
	g2.make_edge();
	g2.solve();
	S[0]=work(0,0);
	S[1]=work(0,1);
	S[2]=work(1,0);
	S[3]=work(1,1);
	vector <pair<int,int>> res;
	res.clear();
	for(int i=0; i<4; i++) {
		if(res.size()<S[i].size()) res=S[i];
	}
	int cnt=0;
	for(auto v:res) {
		cnt++;
		if(cnt<=n*n)
			printf("%d %d\n",v.first-1,v.second-1);
	}
	return 0;
}

T6

贺题有没有贺懂,尝试着讲一下就好了。

我还是太菜了。

考虑枚举第一次操作,枚举一个点 \(u\) 将其接到 \(v\) 上。

此时 \(u\) 肯定不会动了,我们将 \(u\) 作为根(因为根肯定不会动)。

考虑一个点 \(v\),它什么时候要动,它要动当它的 \(fa_{A}\not= fa_{B}\) 时。

因为树根不能动,所以操作都是从叶子开始。

也就是说:如果不去操作一个点 \(v\) 的话,那么 \(v\) 的双亲节点将永远不会改变。

但是如果操作了一个点的话,又把它接回原来的位置显然不优,所以可以得出结论:

  • 一个点被操作,当且仅当它在两棵树中的双亲节点不同。

当一个点的 \(fa_{A}\) 要动时,它肯定要先动,否则 \(fa_{A}\) 无法成为叶子。

当一个点的 \(fa_{B}\) 要动时,它肯定要后动,否则 \(fa_{B}\) 也动不了。

根据以上约束连边,拓扑排序即可。

My Code
#include<bits/stdc++.h>
using namespace std;
const int N=100;
inline int read() {
    int s=1,a=0;char c=getchar();
    while(!isdigit(c)) s=(c=='-')?-s:s,c=getchar();
    while(isdigit(c)) a=(a<<3)+(a<<1)+c-'0',c=getchar();
    return s*a;
}
vector <int> G[3][N];
int n,fa[3][N],deg[3][N];
void dfs(int id,int u,int fat) {
    fa[id][u]=fat;
    for(auto v:G[id][u]) {
        if(v==fa[id][u]) continue;
        dfs(id,v,u);
    }
}
int ans;
int ch[N];
int topsort(int id) {
    queue <int> q;
    int cnt=0;
    for(int i=1; i<=n; i++) {
        if(!deg[id][i]&&ch[i]) q.push(i);
    }
    while(q.size()) {
        int u=q.front();
        q.pop();
        cnt++;
        for(auto v:G[id][u]) {
            --deg[id][v];
            if(!deg[id][v]) {
                q.push(v);
            }
        }
    }
    return cnt;
}
int solve() {
    int num=0;
    memset(ch,0,sizeof(ch));
    for(int i=1; i<=n; i++) {
        if(fa[0][i]!=fa[1][i]) {
            ch[i]=1;
            num++;
        }
    }
    // cout<<1<<endl;
    if(num>ans) return n+1;
    for(int i=1; i<=n; i++) G[2][i].clear();
    memset(deg[2],0,sizeof(deg[2]));
    // cout<<1<<endl;
    for(int i=1; i<=n; i++) {
        if(!ch[i]) {
            if(ch[fa[0][i]]) return n+1;
            continue;
        }
        if(ch[fa[0][i]]) G[2][i].push_back(fa[0][i]),deg[2][fa[0][i]]++;
        if(ch[fa[1][i]]) G[2][fa[1][i]].push_back(i),deg[2][i]++;
    }
    // cout<<1<<endl;
    return topsort(2)==num?num:n+1;
}
int main() {
    int T=read();
    while(T--) {
        n=read();
        memset(deg,0,sizeof(deg));
        for(int i=1; i<=n; i++) G[0][i].clear(),G[1][i].clear();
        for(int i=1; i<n; i++) {
            int u=read(),v=read();
            G[0][u].push_back(v);
            G[0][v].push_back(u);
            deg[0][u]++,deg[0][v]++;
        }
        for(int i=1; i<n; i++) {
            int u=read(),v=read();
            G[1][u].push_back(v);
            G[1][v].push_back(u);
            deg[1][u]++,deg[1][v]++;
        }
        ans=n+1;
        for(int u=1; u<=n; u++) {
            fa[0][u]=fa[1][u]=0;
            // cout<<1<<endl;
            dfs(0,u,0),dfs(1,u,0);
            // cout<<2<<endl;
            ans=min(ans,solve());
            // cout<<3<<endl;
            if(deg[0][u]==1) {
                for(int x=1; x<=n; x++) {
                    if(x!=u) {
                        fa[0][x]=u;
                        dfs(0,x,u);
                        dfs(1,u,0);
                        fa[0][u]=fa[1][u]=0;
                        ans=min(ans,solve()+1);
                    }
                }
            }
        }
        printf("%d\n",ans>n?-1:ans);
    }
    return 0;
}

LJY's

T1

考虑根号分治。

  • \(k\le \sqrt{n}\) 时,显然可以预处理。具体实现就是用一个数组 \(st_{p,k}\) 表示答案,然后如果 \(a_p+p+k>n\) 时就是 \(1\),否则从 \(st_{a_p+p+k,k}\) 转移过来。

  • \(k> \sqrt{n}\) 时,步数绝对不会很多,直接暴力即可。

时间复杂度 \(O(n\displaystyle\sqrt{n})\)

My Code
#include<bits/stdc++.h>
using namespace std;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
const int N=1e5+8;
int st[N][400],a[N];
int n,q,lim;
void init() {
	for(int k=1; k<=lim; k++) {
		for(int i=n; i>=1; i--) {
			if(a[i]+i+k>n) st[i][k]=1;
			else st[i][k]=st[a[i]+i+k][k]+1;
		}
	}
}
int main() {
	n=read();
	for(int i=1; i<=n; i++) a[i]=read();
	lim=sqrt(n);
	init();
	q=read();
	while(q--) {
		int p=read(),k=read();
		if(k<=lim) {
			printf("%d\n",st[p][k]);
		}
		else {
			int ret=0,fp=p;
			while(fp<=n) {
				fp=fp+a[fp]+k;
				ret++;
			}
			printf("%d\n",ret);
		}
	}
	return 0;
}

T2

显然可以想到二分答案。

乍一看似乎无从 check,我们尝试设 \(f_{u}\) 表示 \(u\) 的子树的盈余节点(即没有被染的),最后 check 的方式就是 \(f_1 \le 0\)

考虑如何转移,\(u\) 的盈余节点一定是从 \(v \in son_u\) 中转移过来的。即 \(\displaystyle\sum_{v\in son_u}f_v\)

然后就是当 B 在 \(u\) 时,A 会怎么操作,A 当然会将 \(u\) 的子节点能染得都染了,如果还有多就染其他节点(反正肯定是 \(u\) 的子树),所以还有加上一个 \(|son_u|-k\)

注意此时盈余节点可能为负数,然而儿子转移给父亲时要与 \(0\)\(\max\)

My Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+8;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
int n,f[N];
vector <int> G[N];
void dfs(int u,int fa,int k) {
	int cnt=0;
	for(auto v:G[u]) {
		if(v==fa) continue;
		dfs(v,u,k);
		f[u]+=max(f[v],0);
		cnt++;
	}
	f[u]+=cnt-k;
}
bool check(int k) {
	memset(f,0,sizeof(f));
	dfs(1,0,k);
	return f[1]<=0;
}
int main() {
	n=read();
	for(int i=1; i<n; i++) {
		int u=read(),v=read();
		G[u].push_back(v);
		G[v].push_back(u);
	}
	int l=0,r=n,ans=n;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(check(mid)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}

T3

还是考虑先二分一个 \(r\)

然后考虑 check,设 \(dp_{i,j}\) 表示前 \(i\) 个,能炸毁 \(j\) 个的概率,直接转移即可。

My Code
#include<bits/stdc++.h>
#define x1 fuck
#define x2 fukc
#define y1 aaaa
#define y2 oyyl
using namespace std;
const int N=500;
const double eps=1e-8;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
int n,a[N],k,pp,x[N],y[N];
double pq,p[N],dp[N][N];
double dis(int x1,int x2,int y1,int y2) {
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool check(double r) {
	for(int i=1; i<=n; i++) {
		double d=dis(x[i],x[0],y[i],y[0]);
		if(d<=r) p[i]=1.0;
		else p[i]=exp(1-d*d/r/r);
	}
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int i=1; i<=n; i++) {
		dp[i][0]=dp[i-1][0]*(1-p[i]);
		for(int j=1; j<=i; j++) {
			dp[i][j]=dp[i-1][j]*(1-p[i])+dp[i-1][j-1]*p[i];
		}
	}
	double ans=0;
	for(int i=k; i<=n; i++) ans+=dp[n][i];
	// printf("%.6f\n",ans);
	return ans>=pq;
}
int main() {
	n=read();
	k=read(),pp=read();
	pq=1.0*(1000.00-1.0*pp)/1000.00;
	// printf("%.6f\n",pq);
	x[0]=read(),y[0]=read();
	for(int i=1; i<=n; i++) {
		x[i]=read(),y[i]=read();
	}
	double l=0,r=1e5,ans=0;
	while(r-l>=eps) {
		double mid=(l+r)/2.0;
		if(check(mid)) r=mid,ans=mid;
		else l=mid; 
	}
	printf("%.6f",ans);
	return 0;
}

T4

先将每一行每一列分别排序,然后把同行同列权值相同的点合并,然后将相邻的点连边,用一个超级源点跑拓扑排序,求出每个点的最长路即是答案。

考虑为什么能这么做,首先,同一行或者同一列权值相同的点最后的答案肯定是一样的,可以合并之后一起考虑。

其次,我们要让最大值最小,那么从最小值到最大值就不要有空闲的数,这个我们考虑求出最小值和最大值之间最多差多少个数,这个就是有向图中的最长路。

至于超级源点,你可以把它看做 \(0\)

主要是为了方便 bfs。

My Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+8;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
int n,m,nodcnt,s,deg[N],bfn[N],bfcnt;
struct node {
	int val,id;
	bool operator <(const node &bb) const {
		return val<bb.val;
	}
};
int getpos(int x,int y) {
	return (x-1)*m+y;
}
vector <node> a[N],b[N];
vector <int> G[N];
queue <int> qq;
class UFS {
	private:
		int fa[N],siz[N];
	public:
		void init() {
			for(int i=1; i<=n*m; i++) fa[i]=i,siz[i]=1;
		}
		int find(int x) {
			return fa[x]==x?x:fa[x]=find(fa[x]);
		}
		void merge(int x,int y) {
			int fx=find(x),fy=find(y);
			if(fx==fy) return;
			if(siz[fx]<siz[fy]) swap(fx,fy);
			siz[fx]+=siz[fy];
			fa[fy]=fx;
		}
} dsu;
void work(vector <node> c[N],int w,int h) {
	for(int i=1; i<=w; i++) {
		sort(c[i].begin()+1,c[i].end());
		for(int j=1; j<=h; j++) {
			if(c[i][j].val==c[i][j-1].val) dsu.merge(c[i][j].id,c[i][j-1].id);
		}
	}
}
void mkeg(vector <node> c[N],int w,int h) {
	for(int i=1; i<=w; i++) {
		sort(c[i].begin()+1,c[i].end());
		for(int j=1; j<=h; j++) {
			// printf("%d ",c[i][j].val);
			if(c[i][j].val!=c[i][j-1].val&&j>1) G[dsu.find(c[i][j-1].id)].push_back(dsu.find(c[i][j].id)),deg[dsu.find(c[i][j].id)]++/*,cout<<c[i][j-1].id<<" "<<c[i][j].id<<endl*/;
		}
		// puts("");
	}
}
int main() {
	n=read(),m=read();
	dsu.init();
	for(int i=1; i<=n; i++) {
		a[i].push_back({0,0});
		for(int j=1; j<=m; j++) {
			int x=read();
			nodcnt++;
			a[i].push_back({x,getpos(i,j)});
		}
	}
	for(int i=1; i<=m; i++) {
		b[i].push_back({0,0});
		for(int j=1; j<=n; j++) {
			b[i].push_back(a[j][i]);
		}
	}
	work(a,n,m);
	work(b,m,n);
	mkeg(a,n,m);
	mkeg(b,m,n);
	s=n*m+1;
	for(int i=1; i<=n*m; i++) {
		if(dsu.find(i)==i) {
			// assert(0);
			G[s].push_back(i),deg[i]++;
		}
	}
	qq.push(s);
	while(qq.size()) {
		int x=qq.front();
		qq.pop();
		for(auto v:G[x]) {
			// assert(0);
			deg[v]--;
			bfn[v]=max(bfn[x]+1,bfn[v]);
			// cout<<x<<" -> "<<v<<" by "<<bfn[x]<<endl;
			if(!deg[v]) {
				qq.push(v);
			}
		}
	}
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			// cout<<dsu.find(getpos(i,j))<<" ";
			printf("%d ",bfn[dsu.find(getpos(i,j))]);
		}
		puts("");
	}
	return 0;
}

T5

首先这是个很经典的莫反形式,简单写一下:

\[\sum_{a_1=1}^{i}\cdots\sum_{a_n=1}^{i} [gcd(a_1,\cdots,a_n)=1] \\ =\sum_{a_1=1}^{i}\cdots\sum_{a_n=1}^{i} \sum_{d|gcd(a_1,\cdots,a_n)} \mu (d) \\ =\sum_{d=1}^{i} \mu (d) \sum_{a_1=1}^{i}\cdots\sum_{a_n=1}^{i} \\ =\sum_{d=1}^{i} \mu (d) \bigg\lfloor \frac{i}{d} \bigg\rfloor ^n \\ \]

直接整除分块时间复杂度 \(O(klogn\sqrt{k})\) 的,过不了。

考虑差分。

\(i\) 的答案为 \(f(i)\)

则:

\[f(k)-f(k-1)=\sum_{d=1}^{k} \mu (d) \bigg\lfloor \frac{k}{d} \bigg\rfloor ^n -\sum_{d=1}^{k-1} \mu (d) \bigg\lfloor \frac{k-1}{d} \bigg\rfloor ^n \\ =\sum_{d=1}^{k} \mu (d)\bigg( \bigg\lfloor \frac{k}{d} \bigg\rfloor ^n - \bigg\lfloor \frac{k-1}{d} \bigg\rfloor ^n \bigg) \\ \]

考虑 \(\displaystyle\bigg( \bigg\lfloor \frac{k}{d} \bigg\rfloor ^n - \bigg\lfloor \frac{k-1}{d} \bigg\rfloor ^n \bigg)\) 什么时候会变。

\(d|k\) 时,\(\displaystyle\bigg\lfloor \frac{k}{d} \bigg\rfloor - \bigg\lfloor \frac{k-1}{d} \bigg\rfloor\) 会产生变化。

则枚举 \(d\),枚举倍数,时间复杂度 \(\displaystyle\sum_{d=1}\frac{n}{d} \approx k\log k\),快速幂可以预处理(否则 \(k\log n\log k\) 也不是很好过)。

最后求一下前缀和即可。

时间复杂度 \(O(k\log k + k\log n)\)

My Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Mod=1e9+7,N=2e6+8;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
int prime[N],notprime[N],cnt,mu[N];
int n,kk[N],k,pw[N],cf[N],f[N];
int ksm(int a,int b) {
	int c=1;
	while(b) {
		if(b&1) c*=a,c%=Mod;
		a*=a,a%=Mod;
		b>>=1;
	}
	return c;
}
void init() {
	notprime[0]=notprime[1]=1;
	mu[1]=1;
	for(int i=2; i<=2e6; i++) {
		if(!notprime[i]) {
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1; j<=cnt&&i*prime[j]<=2e6; j++) {
			if(i%prime[j]==0) {
				mu[i*prime[j]]=0;
				notprime[i*prime[j]]=1;
				break;
			}
			mu[i*prime[j]]=mu[i]*mu[prime[j]];
			notprime[i*prime[j]]=1;
		}
	}
}
signed main() {
	n=read(),k=read();
	init();
	for(int i=1; i<=k; i++) {
		pw[i]=ksm(i,n);
	}
	for(int d=1; d<=k; d++) {
		for(int x=1; d*x<=k; x++) {
			cf[d*x]=(cf[d*x]+(mu[d]+Mod)%Mod*(pw[x])%Mod)%Mod;
			cf[d*x]=(cf[d*x]-(mu[d]+Mod)%Mod*pw[x-1]%Mod+Mod)%Mod;
		}
	}
	int ans=0;
	for(int i=1; i<=k; i++) {
		f[i]=(f[i-1]+cf[i])%Mod;
		ans=(ans+(f[i]^i)%Mod)%Mod;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-11-03 13:11  redproblemdog  阅读(39)  评论(0编辑  收藏  举报