• Comet OJ Contest #13 简要题解


    C2

    首先用并查集维护(1)的连通块,然后用另外一个并查集维护第(i)行中,第(j)列之后的第一个(0)的位置,就是如果当前位置是(1)那么它的父亲是它右边的格子,否则是它自己。

    时间复杂度(O(nmlog m+nq))

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    const int N = 1003, d[2][4] = {{0, 1, 0, -1}, {1, 0, -1, 0}};
    int n, m, q, fa[N][N], cnt, Fa[N * N], Siz[N * N];
    char str[N];
    bool a[N][N];
    inline int getfa(int x, int y){
    	return (fa[x][y] == y) ? y : (fa[x][y] = getfa(x, fa[x][y]));
    }
    inline int Getfa(int x){
    	return (Fa[x] == x) ? x : (Fa[x] = Getfa(Fa[x]));
    }
    inline int id(int x, int y){return (x - 1) * m + y;}
    inline void work(int x, int y){
    	for(Rint i = 0;i < 4;i ++){
    		int nx = x + d[0][i], ny = y + d[1][i];
    		if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && a[nx][ny]){
    			int fa1 = Getfa(id(x, y)), fa2 = Getfa(id(nx, ny));
    			if(fa1 != fa2){
    				if(Siz[fa1] > Siz[fa2]) swap(fa1, fa2);
    				Fa[fa1] = fa2; Siz[fa2] += Siz[fa1]; -- cnt;
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d", &n, &m);
    	for(Rint i = 1;i <= n * m;i ++) Fa[i] = i, Siz[i] = 1;
    	for(Rint i = 1;i <= n;i ++){
    		scanf("%s", str + 1);
    		for(Rint j = 1;j <= m;j ++){
    			a[i][j] = (str[j] == '1'); fa[i][j] = j + a[i][j]; cnt += a[i][j];
    		}
    		fa[i][m + 1] = m + 1;
    	}
    	for(Rint i = 1;i <= n;i ++)
    		for(Rint j = 1;j <= m;j ++)
    			if(a[i][j]) work(i, j);
    	scanf("%d", &q);
    	while(q --){
    		int x1, y1, x2, y2;
    		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    		for(Rint i = x1;i <= x2;i ++){
    			int j = y1;
    			while((j = getfa(i, j)) <= y2){
    				++ cnt; a[i][j] = 1; fa[i][j] = j + 1;
    				work(i, j);
    			}
    		}
    		printf("%d
    ", cnt);
    	}
    }
    

    D

    首先甩上来一个单位根反演。

    [F_n=frac{1}{2}((b+sqrt a)^n+(b-sqrt a)^n) ]

    于是你发现(p)不是质数,于是就自闭了。

    然后你想起了BJ人民的扩域做法,就是搞一个域(Z[sqrt a]={x+ysqrt a|x,yin Z}),然后直接计算快速幂。但是你绝望地发现,最后有一个(frac{1}{2}),于是你又废了。

    然后你去看题解,发现逆向思维是非常重要的。平常大家都是递推公式( ightarrow)通项公式,但是你有想到通项公式( ightarrow)递推公式吗?

    根据特征方程的理论,设(F_n=AF_{n-1}+BF_{n-2}),那么(x^2-Ax-B=0)的两根为(bpm sqrt a),所以(A=b+sqrt a+b-sqrt a=2b)(B=-(b+sqrt a)(b-sqrt a)=a-b^2)。然后用矩阵快速幂求出(F_n)就可以了。

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    typedef long long LL;
    int T;
    LL a, b, p, n;
    inline void upd(LL &a, LL b){a += b; if(a >= p) a -= p;}
    struct Matrix {
    	LL x[2][2];
    	inline Matrix(){memset(x, 0, sizeof x);}
    	inline Matrix operator = (const Matrix &o){
    		memcpy(x, o.x, sizeof x);
    		return *this;
    	}
    	inline Matrix operator * (const Matrix &o) const {
    		Matrix res;
    		for(Rint i = 0;i < 2;i ++)
    			for(Rint k = 0;k < 2;k ++)
    				for(Rint j = 0;j < 2;j ++)
    					upd(res.x[i][j], (__int128) x[i][k] * o.x[k][j] % p);
    		return res;
    	}
    } A, B;
    inline Matrix kasumi(Matrix A, LL b){
    	Matrix res; res.x[0][0] = res.x[1][1] = 1;
    	while(b){
    		if(b & 1) res = res * A;
    		A = A * A; b >>= 1;
    	}
    	return res;
    }
    int main(){
    	scanf("%d", &T);
    	while(T --){
    		scanf("%lld%lld%lld%lld", &n, &a, &b, &p); a %= p; b %= p;
    		if(n == 0){puts("1"); continue;}
    		if(n == 1){printf("%lld
    ", b); continue;}
    		A.x[0][0] = 2 * b % p; A.x[0][1] = (a + p - (__int128) b * b % p) % p;
    		A.x[1][0] = 1; B.x[0][0] = b; B.x[1][0] = 1;
    		B = kasumi(A, n - 1) * B;
    		printf("%lld
    ", B.x[0][0]);
    	}
    }
    

    E

    这是一个看上去很吓人的计算几何。

    首先我们发现,这个范围实际上就是两个相距(2d)的平行线之间的部分,于是我们枚举其中一个平行线,发现它总能经过一个点,我们枚举这个点(A),看它在哪些时候可以覆盖另外一个点(B)

    分类讨论。

    1. (dis(A,B)le 2d),设(alpha=arctanfrac{y_B-y_A}{x_B-x_A}),则直线倾角的范围是([alpha,alpha+pi])

    2. (dis(A,B)>2d),设(eta=arcsinfrac{2d}{dis(A,B)}),则直线倾角的范围是([alpha,alpha+eta]cup[alpha+pi-eta,alpha+pi])

    如果看不懂的话,就来看个图。

    于是问题就转化成了给出一堆区间,求一个点至多被覆盖多少次。这是一个经典题

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    const int N = 16003;
    const double PI = acos(-1);
    int n, d, x[N], y[N], ans, tag[N], len, tot;
    double l[N], r[N], val[N];
    inline void add(double x, double y){
    	if(x < 0 && y < 0) x += PI * 2, y += PI * 2;
    	if(x < 0){
    		l[++ tot] = x + 2 * PI; r[tot] = 2 * PI; val[++ len] = x + 2 * PI; val[++ len] = 2 * PI;
    		l[++ tot] = 0; r[tot] = y; val[++ len] = 0; val[++ len] = y;
    	} else {
    		l[++ tot] = x; r[tot] = y; val[++ len] = x; val[++ len] = y;
    	}
    }
    inline void work(int p){
    	len = tot = 0;
    	for(Rint i = 1;i <= n;i ++) if(i != p){
    		double alpha = atan2(y[i] - y[p], x[i] - x[p]), dis = (x[i] - x[p]) * (x[i] - x[p]) + (y[i] - y[p]) * (y[i] - y[p]);
    		if(dis <= d * d){
    			add(alpha, alpha + PI);
    		} else {
    			double beta = asin(1.0 * d / sqrt(dis));
    			add(alpha, alpha + beta); add(alpha + PI - beta, alpha + PI);
    		}
    	}
    	sort(val + 1, val + len + 1);
    	len = unique(val + 1, val + len + 1) - val - 1;
    	for(Rint i = 1;i <= tot;i ++){
    		int a = lower_bound(val + 1, val + len + 1, l[i]) - val, b = lower_bound(val + 1, val + len + 1, r[i]) - val + 1;
    		++ tag[a]; -- tag[b];
    	}
    	for(Rint i = 1;i <= len;i ++){
    		ans = max(ans, tag[i] += tag[i - 1]); tag[i - 1] = 0;
    	} tag[len] = 0;
    }
    int main(){
    	scanf("%d%d", &n, &d); d <<= 1;
    	for(Rint i = 1;i <= n;i ++) scanf("%d%d", x + i, y + i);
    	for(Rint i = 1;i <= n;i ++) work(i);
    	printf("%d", ans + 1);
    }
    
  • 相关阅读:
    JSP第六次作业
    JSP第五次作业
    第二次软件测试作业
    JSP第四次作业(2)
    JSP第四次作业(1)
    JSP第七次作业
    JSP第六次作业
    session对象练习
    JSP第四次作业(2)
    JSP第四次作业(1)
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/11755494.html
Copyright © 2020-2023  润新知