topcoder srm 410 div1

problem1 link

不包含$gridConnections$ 的联通块一定是连在所有包含$gridConnections$的联通块中最大的那一块上。

import java.util.*;
import java.math.*;
import static java.lang.Math.*;

public class AddElectricalWires {

	static class UnionSet {
		public int[] father=null;
		public int[] size=null;
		public int[] edges=null;

		public UnionSet(int n) {
			father=new int[n];
			size=new int[n];
			edges=new int[n];
			for(int i=0;i<n;++i) {
				father[i]=i;
				size[i]=1;
			}
		}

		public int getFather(int x) {
			if(father[x]==x) {
				return x;
			}
			return father[x]=getFather(father[x]);
		}
		public UnionSet merge(int x,int y) {
			int fx=getFather(x);
			int fy=getFather(y);
			if(fx!=fy) {
				father[fx]=fy;
				size[fy]+=size[fx];
				edges[fy]+=edges[fx]+1;
			}
			else {
				edges[fy]+=1;
			}
			return this;
		}
	}

	public int maxNewWires(String[] wires,int[] gridConnections) {
		final int n=wires.length;
		UnionSet unionSet=new UnionSet(n);
		for(int i=0;i<n;++i) {
			for(int j=i+1;j<n;++j) {
				if(wires[i].charAt(j)=='1') {
					unionSet.merge(i,j);
				}
			}
		}


		int result=0;
		long visited=0;
		int maxSize=0;
		for(int i=0;i<gridConnections.length;++i) {
			int t=gridConnections[i];
			int ft=unionSet.getFather(t);
			int sz=unionSet.size[ft];
			result+=sz*(sz-1)/2-unionSet.edges[ft];
			visited|=1l<<ft;
			if(sz>maxSize) {
				maxSize=sz;
			}
		}
		for(int i=0;i<n;++i) {
			if(i==unionSet.getFather(i)&&(0==(visited&(1l<<i)))) {
				int sz=unionSet.size[i];
				result+=sz*(sz-1)/2-unionSet.edges[i];
				result+=sz*maxSize;
				maxSize+=sz;
			}
		}
		return result;
	}
}

  

problem2 link

每个$base$的选择一定是$addresses[i]$或者$addresses[i]-k+1$。这样进行动态规划即可。

import java.util.*;
import java.math.*;
import static java.lang.Math.*;

public class ContiguousCache {
	
	public long minimumReads(int n, int k, int[] addresses) {
		List<Integer> list=new ArrayList<>();
		final int m=addresses.length;
		for(int i=0;i<m;++i) {
			final int t=addresses[i];
			if(t-k+1>=0) {
				list.add(t-k+1);
			}
			if(t<=n-k) {
				list.add(t);
			}
			else {
				list.add(n-k);
			}

		}
		int[] a=unique(list);

		final int p=a.length;
		long[][] f=new long[m][p];
		for(int i=0;i<m;++i) {
			for(int j=0;j<p;++j) {
				f[i][j]=-1;
			}
		}
		for(int i=0;i<p;++i){
			if(a[i]<=addresses[0]&&addresses[0]<a[i]+k) {
				f[0][i]=Math.min(n,a[i]+k-1)-a[i]+1;
			}
		}


		for(int i=1;i<m;++i) {
			final int r=addresses[i];
			for(int j=0;j<p;++j) {
				if(f[i-1][j]==-1) {
					continue;
				}

				for(int t=0;t<p;++t) {

					if(a[t]<=r&&r<a[t]+k) {
						long cost=f[i-1][j]+calCost(a[j],a[t],k);
						if(f[i][t]==-1||f[i][t]>cost) {
							f[i][t]=cost;
						}
					}
				}

			}
		}


		long result=-1;
		for(int i=0;i<p;++i) {
			if(f[m-1][i]==-1) {
				continue;
			}
			if(result==-1||result>f[m-1][i]) {
				result=f[m-1][i];
			}
		}
		return result;
	}


	int calCost(int p1,int p2,int k) {
		if(p1<p2) {
			if(p1+k<=p2) {
				return k;
			}
			return p2-p1;
		}
		else if(p1==p2) {
			return 0;
		}
		else {
			if(p2+k<=p1) {
				return k;
			}
			return p1-p2;
		}
	}

	int[] unique(List<Integer> list) {
		Collections.sort(list);
		int c=1;
		int pre=0;
		for(int i=1;i<list.size();++i) {
			if(list.get(i)==list.get(pre)) {
				continue;
			}
			++c;
			pre=i;
		}
		int[] a=new int[c];
		a[0]=list.get(0);
		c=1;
		for(int i=1;i<list.size();++i) {
			if(a[c-1]!=list.get(i)) {
				a[c++]=list.get(i);
			}
		}
		return a;
	}
}

  

problem3 link

首先进行梯形剖分。对于变形每条边$p,q$,分别向$x$轴做垂线,该边与垂线以及$x$轴可以组成一个梯形。对于$n$条边$n$个梯形来说,注意有的取正值有的取负值加起来就能得到整个内部的点个数。

对于一个梯形来说,内部点可以用公式$\sum_{i=0}^{n-1}\left \lfloor \frac{a+di}{m} \right \rfloor$来进行计算。

这里可以认为$0\leq a < m,0<b<m$,否则可以直接提出到外面进行计算。

对于某个$i$,$\left \lfloor \frac{a+di}{m} \right \rfloor$的值可以看作垂线$(i,0),(i,a+di)$与水平线$y=km$的交点个数,$1\leq k \leq \left \lfloor \frac{a+di}{m} \right \rfloor$

到这里可以换个角度计算。对于每条水平线有多少垂线与其有交点。按照这个思路,可转化计算的公式:

$\sum_{i=0}^{n-1}\left \lfloor \frac{a+di}{m} \right \rfloor$=$\sum_{k=0}^{L-1}\left \lfloor \frac{(a+dn)mod(m)+mk}{d} \right \rfloor$,其中$L=\left \lfloor \frac{a+dn}{m} \right \rfloor$

中间的推导就不写了。

等号后面的式子跟前面形式类似,所以可以继续转化,每次求和项都在减少。

import java.util.*;
import java.math.*;
import static java.lang.Math.*;

public class WifiPlanet {
	
	public long routersNeeded(int[] x, int[] y, int denom) {
		final int n=x.length;
		long result=0;
		for(int i=0;i<n;++i) {
			result+=cal(x[i],y[i],x[(i+1)%n],y[(i+1)%n],denom);
		}
		if(result<0) {
			result=-result;
		}
		return result;
	}

	long cal(long x1,long y1,long x2,long y2,long m) {
		if(x1==x2) {
			return 0;
		}
		if(x1>x2) {
			return -cal(x2,y2,x1,y1,m);
		}
		long L=(x1+m-1)/m*m;
		long R=(x2-1)/m*m;
		long n=(R-L)/m+1;
		return dfs((y1*(x2-x1)+(y2-y1)*(L-x1))/m,y2-y1,n,x2-x1);
	}

	long dfs(long a,long d,long n,long m) {
		if(n==0) {
			return 0;
		}
		if(d==0) {
			return a/m*n;
		}
		if(d<0) {
			return dfs(a+d*(n-1),-d,n,m);
		}
		if(a>=m) {
			return dfs(a%m,d,n,m)+a/m*n;
		}
		if(d>=m) {
			return dfs(a,d%m,n,m)+(d/m)*n*(n-1)/2;
		}
		return dfs((a+n*d)%m,m,(a+n*d)/m,d);
	}

}

  

 

posted @ 2017-09-17 22:03  朝拜明天19891101  阅读(312)  评论(0编辑  收藏  举报