• 清华集训2015-Day 2


    校内测试做到了,于是就把解题报告发出来。

    简单回路

    一个 (n imes m) 的方格纸,有 (k) 个障碍点。(q) 次询问,每次询问 ((x,y)) ,问有多少条简单回路经过 ((x,y)-(x+1,y)) 这条边。(nle 1000,mle 6,kle 100,qle 10000)

    分析

    场上 AC 了这题。

    暴力的想法是每次询问都dp一次,到询问点的时候就强制有那个插头。由此就可以想到,如果可以快速转移完后面的所有格子,那么就可以只进行一次dp,经过一个格子之后就把含有这个插头的状态都快速转移一下,就可以直接预处理出每个 ((x,y)) 的答案了。

    因此,我们只需要求出 p[x][y][s] 数组表示 ((x,y)) 这个位置状态为 (s) ,最后对答案的贡献系数。这是可以反过来转移求的。

    因此总复杂度为 (O(nm|s|))

    代码

    场上我的写法是一行行来做,所以复杂度为 (O(nm|s|^2)) ,也是可以过的。这是场上的代码 。

    #include<bits/stdc++.h>
    #define M(x) memset(x,0,(sizeof x[0])*maxs)
    using namespace std;
    typedef long long giant;
    inline char nchar() {
    	static const int bufl=1<<20;
    	static char buf[bufl],*a,*b;
    	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
    }
    inline int read() {
    	int x=0,f=1;
    	char c=nchar();
    	for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
    	return f>0?x:-x;
    }
    const int maxn=1e3+5;
    const int q=1e9+7;
    const int maxs=200;
    const int maxv=1<<16|1;
    const int maxm=8;
    inline int Plus(int x,int y) {return (x+=y)>=q?x-q:x;}
    inline void Pe(int &x,int y) {x=Plus(x,y);}
    inline int Multi(int x,int y) {return (giant)x*y%q;}
    inline void Me(int &x,int y) {x=Multi(x,y);}
    inline int get(int x,int p) {return (x>>(p<<1))&3;}
    inline int mod(int x,int p,int d) {return (x&(~(3<<(p<<1))))+(d<<(p<<1));}
    bool no[maxn][maxm];
    int ans[maxn][maxm],stat[maxs],n,m,all=0,id[maxv],mat[maxs][maxm],ids=0,blo;
    int tra[maxn][maxs],ader,to[maxs],F[2][maxs],*f=F[0],*g=F[1]; // to : how much transite to s
    void dec(int x) {
    	for (int i=1;i<=m+1;++i) printf("%d ",get(x,i));
    	puts("");
    }
    inline void match(int x,int mt[]) {
    	static int sta[maxm];
    	int top=0;
    	for (int i=1;i<=m+1;++i) {
    		const int d=get(x,i);
    		if (d==1) sta[++top]=i; else if (d==2) {
    			int y=sta[top--];
    			mt[y]=i,mt[i]=y;
    		}
    	}
    }
    inline bool ok(int x) {
    	int cnt=0;
    	for (int i=1;i<=m+1;++i) {
    		const int d=get(x,i);
    		if (d==1) ++cnt; else if (d==2) --cnt;
    		if (cnt<0) return false;
    	}
    	return cnt==0;
    }
    void dfs(int now,int x) {
    	if (now>m+1) {
    		if (ok(x)) {
    			stat[id[x]=++ids]=x;
    			match(x,mat[ids]);
    		}
    		return;
    	}
    	for (int i=0;i<3;++i) dfs(now+1,mod(x,now,i));
    }
    void predp(int row,int st) {
    	M(f),M(g);
    	f[st]=1;
    	for (int j=1;j<=m;++j) {
    		swap(f,g),M(f);
    		for (int s=1;s<=ids;++s) if (g[s]) {
    			const int &d=stat[s],&w=g[s],x=get(d,j),y=get(d,j+1),c=mod(mod(d,j,0),j+1,0),*mt=mat[s];
    			if (no[row][j]) {
    				if (x==0 && y==0) Pe(f[id[d]],w);
    				continue;
    			}
    			if (x==0 && y==0) {
    				Pe(f[id[d]],w);
    				Pe(f[id[mod(mod(c,j,1),j+1,2)]],w);
    			} else if (x==0 || y==0) {
    				Pe(f[id[mod(c,j,x+y)]],w);
    				Pe(f[id[mod(c,j+1,x+y)]],w);
    			} else if (x==1 && y==1) {
    				Pe(f[id[mod(c,mt[j+1],1)]],w);
    			} else if (x==2 && y==2) {
    				Pe(f[id[mod(c,mt[j],2)]],w);
    			} else if (x==1 && y==2) {
    				if (!c) Pe(ader,w);
    			} else if (x==2 && y==1) {
    				Pe(f[id[c]],w);
    			}
    		}
    	}
    	for (int s=1;s<=ids;++s) if (get(stat[s],m+1)==0 && f[s]) Pe(to[id[stat[s]<<2]],f[s]);
    }
    void prepro() {
    	dfs(1,0);
    	for (int i=n;i;--i) {
    		for (int d=1;d<=ids;++d) if (get(stat[d],1)==0) {
    			ader=0;
    			memset(to,0,sizeof to);
    			predp(i,d);
    			tra[i][d]=ader;
    			for (int j=1;j<=ids;++j) if (to[j]) Pe(tra[i][d],Multi(to[j],tra[i+1][j]));
    		}
    	}
    }
    void work() {
    	M(f),M(g);
    	f[id[0]]=1;
    	for (int i=1;i<=n;++i) {
    		swap(f,g),M(f);
    		for (int s=1;s<=ids;++s) if (g[s]) {
    			const int &d=stat[s],&w=g[s];
    			if (get(d,m+1)==0) Pe(f[id[d<<2]],w);
    		}
    		for (int s=1;s<=ids;++s) if (f[s]) {
    			int tmp=Multi(f[s],tra[i][s]);
    			for (int j=2;j<=m+1;++j) if (get(stat[s],j)) Pe(ans[i-1][j-1],tmp);
    		}
    		for (int j=1;j<=m;++j) {
    			swap(f,g),M(f);
    			for (int s=1;s<=ids;++s) if (g[s]) {
    				const int &d=stat[s],&w=g[s],x=get(d,j),y=get(d,j+1),c=mod(mod(d,j,0),j+1,0),*mt=mat[s];
    				if (no[i][j]) {
    					if (x==0 && y==0) Pe(f[id[d]],w);
    					continue;
    				}
    				if (x==0 && y==0) {
    					Pe(f[id[mod(mod(c,j,1),j+1,2)]],w);
    					Pe(f[id[d]],w);
    				} else if (x==0 || y==0) {
    					Pe(f[id[mod(c,j,x+y)]],w);
    					Pe(f[id[mod(c,j+1,x+y)]],w);
    				} else if (x==1 && y==1) {
    					Pe(f[id[mod(c,mt[j+1],1)]],w);
    				} else if (x==2 && y==2) {
    					Pe(f[id[mod(c,mt[j],2)]],w);
    				} else if (x==2 && y==1) {
    					Pe(f[id[c]],w);
    				} // we don't process (x=1,y=2) since this will be add to answer on the next row
    			}
    		}
    	}
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	n=read(),m=read(),blo=read();
    	for (int i=1;i<=blo;++i) {
    		int x=read(),y=read();
    		no[x][y]=true;
    	}
    	prepro();
    	work();
    	int req=read();
    	while (req--) {
    		int x=read(),y=read();
    		printf("%d
    ",ans[x][y]);
    	}
    	return 0;
    }
    

    卡常数

    空间中有 (n) 个点,有两种操作:

    • 询问 ((x,y,z,r)) ,问空间中哪个点在以 ((x,y,z)) 为球心,(r) 为半径的球面上。保证答案唯一。
    • 修改 ((i,x,y,z)) ,把 (i) 号点的坐标改为 ((x,y,z))

    询问有加密。初始时 last=0.1 ,给出两个值 (a,b (0le b<a<5)),对于每次询问的 ((x,y,z,r))((i,x,y,z)) ,设被加密的值为 (x) ,那么给出的值为 (f(last*x+1)) ,其中 (f(x)=ax-bsin x)

    所有坐标随机。

    (n,mle 65536)

    分析

    从未见过如此奇怪的加密方式,要二分答案才能得到原值。

    大部分人的做法是 KDTree 。

    题解做法很神秘,启发性很大。一般的加密是为了强制在线,而有时候这种加密却可以帮助我们直接求出答案!

    主要是利用了 (iin mathbb Z,iin [1,n]) 的性质。稍微看了一下,感觉复杂度有点小问题,并没有仔细研究。

    矩阵变换

    有一个 (n imes m) 的矩阵,满足:

    • (m>n)
    • 每行 ([1,n]) 出现恰好一次,其他位置均为 0
    • 每列 ([1,n]) 每个数最多出现一次

    现在要在每行中选出一个数 (x) ,并把这个数后面的所有位置都变为 (x) 。要求是保持第三个条件。

    (n,mle 400,Tle 50)

    分析

    这题很妙啊!

    可以发现,每行选出的数 (b_i) 一定是 (n) 的一个排列。也就是说,这建立了一个 行 到 数 的匹配。

    什么条件下第三个条件不满足呢?

    C

    若令行喜欢靠前的数,数喜欢令它靠后的行,那么这其实就是一个稳定婚姻问题。依次选择即可。

    复杂度为 (O(nm))

    代码

    #include<bits/stdc++.h>
    #define M(x) memset(x,0,sizeof x)
    using namespace std;
    inline char nchar() {
        static const int bufl=1<<20;
        static char buf[bufl],*a,*b;
        return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
    }
    inline int read() {
        int x=0,f=1;
        char c=nchar();
        for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
        for (;isdigit(c);c=nchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=201,maxm=401;
    int a[maxn][maxm],n,m,st[maxn],num[maxn],b[maxn],wh[maxn][maxn],sta[maxn],top;
    void work() {
        n=read(),m=read();
        for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) wh[i][a[i][j]=read()]=j;
        fill(st+1,st+n+1,1);
        for (int i=1;i<=n;++i) sta[i]=i;
        M(num),M(b),top=n;
        while (top) {
            int x=sta[top];
            for (int &i=st[x];i<=m;++i) if (a[x][i]) {
                int &v=a[x][i];
                if (!num[v] || wh[num[v]][v]<i) {
                    if (num[v]) b[num[v]]=0,sta[top]=num[v]; else --top;
                    num[v]=x,b[x]=v,++i;
                    break;
                }
            }
        }
        for (int i=1;i<=n;++i) printf("%d%c",b[i]," 
    "[i==n]);
    }
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("test.in","r",stdin);
    #endif
        for (int T=read();T--;) work();
        return 0;
    }
    
  • 相关阅读:
    SDWebImage 原理及使用问题
    iOS沙盒目录
    java基础知识积累总结
    切片原型[start:stop:step]
    select嵌套问题
    Numpy:字符串转为数值型、日期型;日期型转为数值型
    数据标准化处理(简单整理)
    Numpy:自定义复合数据类型
    Numpy:数组创建 numpy.arrray() , numpy.arange()、np.linspace ()、数组基本属性
    Numpy:使用索引、切片提取一维数组、多维数组内数据
  • 原文地址:https://www.cnblogs.com/owenyu/p/7623826.html
Copyright © 2020-2023  润新知