TopCoder SRM 429 Summary

SubrectanglesOfTable
简单统计题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
class SubrectanglesOfTable
{
public:
	vector<long long> getQuantity(vector <string> table)
	{
		vector <long long> r(26);
		int m=table.size();
		for(int i=0;i<m;++i)
		{
			table[i]+=table[i];
			table.push_back(table[i]);
		}
		m<<=1;
		int n=table[0].size();
		for(long long i=0;i<m;++i)
			for(long long j=0;j<n;++j)
				r[table[i][j]-'A']+=(i+1)*(j+1)*(m-i)*(n-j);
		return r;
	}
};

IngredientProportions
构造
随便给某个原料(比如原料0)设定一个质量值,由于题目保证有唯一解,通过给出的比例关系一定可以求出其它所有原料的质量,最后将所有原料的质量统一放大或缩小到最简整数比即可得到正确解。为避免分数运算,我们可以给原料0分配一个恰当的整数质量使得以后的运算中不出现分数。先运用类似Floyd的方法求出原料0与其它所有原料的直接比例,求完后假设原料0与任意原料k的质量比为A[k]:B[k],那么为使求得的原料k的质量为整数,原料0的质量必须为A[k]的倍数,因此我们应该给原料0分配A[1..N-1]所有数的最小公倍数这么大的质量。这样,我们得到了所有原料的整数质量,最后求出它们的最大公约数而后化为最简比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
int p[11][11][2];
long long f[11];
 
long long gcd(long long a,long long b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}
 
class IngredientProportions
{
public:
	vector <int> getMasses(vector <string> d)
	{
		memset(p,0,sizeof(p));
		int n=d.size()+1;
		for(int i=0;i<d.size();++i)
		{
			int a=d[i][1]-'0',b=d[i][8]-'0',r=d[i][13]-'0',s=d[i][15]-'0';
			p[a][b][0]=r;
			p[a][b][1]=s;
			p[b][a][0]=s;
			p[b][a][1]=r;
		}
		for(int k=0;k<n;++k)
			for(int i=0;i<n;++i)
				if(p[i][k][0])
					for(int j=0;j<n;++j)
						if(p[k][j][0]&&!(p[i][j][0]))
						{
							p[i][j][0]=p[i][k][0]*p[k][j][0];
							p[i][j][1]=p[i][k][1]*p[k][j][1];
						}
		f[0]=1;
		for(int i=1;i<n;++i) f[0]=f[0]/gcd(f[0],p[0][i][0])*p[0][i][0];
		for(int i=1;i<n;++i) f[i]=f[0]/p[0][i][0]*p[0][i][1];
		long long g=f[0];
		for(int i=1;i<n;++i) g=gcd(g,f[i]);
		vector <int> r;
		for(int i=0;i<n;++i) r.push_back(f[i]/g);
		return r;
	}
};

SpecificPolyominoCovering
构造
本题的难点在于如何快速判断一个棋盘能否用给定的两种棋子填满。
注意到所有的AB嵌套棋子
ABBA
AAAA
都可以用4个B型棋子
BBBB
BBBB
替换,所以在判定时我们可以禁止AB嵌套棋子,而不失正确性。这样我们可以从上到下,从左到右扫描棋盘,每次遇到空缺先尝试B棋子再尝试A棋子,如果都不成功则判定失败,否则继续判定。(详细的正确性证明请参考官方题解)
注意到我们的判定过程实际上构造了一组解,但题目要求我们返回字典序最小的解。我们可以多次利用上述判定过程,仍然从上到下,从左到右扫描棋盘,每次遇到空缺先尝试A棋子,如果可以成功放进去A棋子并且调用上述过程判定剩下的棋盘可以填满棋子,那么就把A棋子真正放到棋盘上,否则就放B棋子,这样可以正确得出字典序最小的解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
int r,c;
bool u[55][55];
 
bool able(const vector <string> &g)
{
	memset(u,false,sizeof(u));
	for(int i=0;i<r;++i)
		for(int j=0;j<c;++j)
			if(g[i][j]=='X'&&!u[i][j])
			{
				if(j+1<c&&g[i][j+1]=='X') u[i][j+1]=true;
				else if(i+1<r&&j+3<c&&g[i][j+3]=='X'&&g[i+1][j]=='X'&&g[i+1][j+1]=='X'&&g[i+1][j+2]=='X'&&g[i+1][j+3]=='X') u[i][j+3]=u[i+1][j]=u[i+1][j+1]=u[i+1][j+2]=u[i+1][j+3]=true;
				else return false;
			}
	return true;
}
 
class SpecificPolyominoCovering
{
public:
	vector <string> findCovering(vector <string> g)
	{
		r=g.size();
		c=g[0].size();
		if(!able(g)) return vector <string>();
		for(int i=0;i<r;++i)
			for(int j=0;j<c;++j)
				if(g[i][j]=='X')
				{
					if(i+1<r&&j+3<c&&g[i][j+3]=='X'&&g[i+1][j]=='X'&&g[i+1][j+1]=='X'&&g[i+1][j+2]=='X'&&g[i+1][j+3]=='X')
					{
						g[i][j]=g[i][j+3]=g[i+1][j]=g[i+1][j+1]=g[i+1][j+2]=g[i+1][j+3]='A';
						if(able(g)) continue;
						else g[i][j]=g[i][j+3]=g[i+1][j]=g[i+1][j+1]=g[i+1][j+2]=g[i+1][j+3]='X';
					}
					g[i][j]=g[i][j+1]='B';
				}
		return g;
	}
};
Posted in SRM, TopCoder. Tags: , , . No Comments »

TopCoder SRM 431 Summary

LaserShooting
简单概率题
显然本题中target并不阻挡射线,所以各个targe之间互不影响,那么射中个数的数学期望值就是射中各个targe的概率和。值得注意的是C++中求反正切arctan时最好用atan2(y,x)函数,因为它不仅能处理斜率不存在的情况还能正确处理角度符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
const double PI=3.1415926535;
 
class LaserShooting
{
public:
	double numberOfHits(vector <int> x, vector <int> y1, vector <int> y2)
	{
		int n=x.size();
		for(int i=0;i<n;++i)
			if(y1[i]>y2[i])
				swap(y1[i],y2[i]);
		double ans=0;
		for(int i=0;i<n;++i) ans+=(atan2(y2[i],x[i])-atan2(y1[i],x[i]))/PI;
		return ans;
	}
};

MegaCoolNumbers

动态规划
首先挖掘一下cool number of power A这个定义的本质:一个N位数如果是cool number of power A(A9时直接返回0,下面我们考虑A<=9的情况。采用动态规划,f[n][i][last][diff]表示所有n位数中最小power值为i且最后一个数字为last且最后一段的公差为diff的mega cool numbers个数(其中0<=diff<=8,用diff=9表示最后一组仅包含last这一个数字的情况,表示此时公差不确定),按照题意容易想出状态转移方法,这里不赘述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
const int MOD=1000000007;
int f[1001][10][10][10];
 
class MegaCoolNumbers
{
public:
	int count(int n,int A)
	{
		if(A>9) return 0;
		memset(f,0,sizeof(f));
		for(int i=1;i<10;++i) f[1][1][i][9]=1;
		for(int i=2;i<=n;++i)
			for(int j=1;j<10;++j)
				for(int last=1;last<10;++last)
				{
					for(int diff=0;diff<last;++diff) f[i][j][last][diff]=(f[i-1][j][last-diff][diff]+f[i-1][j][last-diff][9])%MOD;
					for(int prev=1;prev<=last;++prev)
						for(int diff=0;diff<9;++diff)
							if(diff!=last-prev)
								f[i][j][last][9]=(f[i][j][last][9]+f[i-1][j-1][prev][diff])%MOD;
				}
		int ans=0;
		for(int i=1;i<10;++i)
			for(int j=0;j<10;++j)
				ans=(ans+f[n][A][i][j])%MOD;
		return ans;
	}
};
Posted in SRM, TopCoder. Tags: , , . No Comments »

TopCoder SRM 432 Summary

LampsGrid
枚举(思维难度大)
每次操作改变一整列的状态,所以如果初始时某两行状态相同,那么经过任意次操作后这两行的状态仍然相同;如果初始时某两行状态不相同,那么经过任意次操作后这两行的状态仍然不相同。枚举每一行,考虑能否把它通过K次操作变为全亮,如果能的话再数一数有多少行和这一行初始状态相同,它们也能通过同样的K次操作变为全亮。找出能变为全亮的且与之状态相同的行数最多的行即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <cmath>
#include <ctime>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <string>
#include <vector>
 
using namespace std;
 
class LampsGrid
{
public:
	int mostLit(vector <string> initial, int K)
	{
		int ans=0;
		for(int i=0;i<initial.size();++i)
		{
			int num=0;
			for(int j=0;j<initial[i].size();++j) num+=initial[i][j]=='0';
			if(num>K||(K-num)%2) continue;
			num=0;
			for(int j=0;j<initial.size();++j) num+=initial[i]==initial[j];
			ans=max(ans,num);
		}
		return ans;
	}
};

TopCoder SRM 435 Summary

CellRemoval
简单题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <cmath>
#include <ctime>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <map>
#include <algorithm>
#include <vector>
 
using namespace std;
 
int c[55][55],num[55];
 
void dfs(int v)
{
	if(c[v][0]==0)
	{
		num[v]=1;
		return;
	}
	dfs(c[v][1]);
	dfs(c[v][2]);
	num[v]=num[c[v][1]]+num[c[v][2]];
}
 
class CellRemoval
{
public:
    int cellsLeft(vector <int> parent, int deletedCell)
    {
		memset(c,0,sizeof(c));
		memset(num,0,sizeof(num));
		int n=parent.size();
		int root=-1;
		for(int i=0;i<n;++i)
			if(parent[i]==-1) root=i;
			else c[parent[i]][++c[parent[i]][0]]=i;
		dfs(root);
		return num[root]-num[deletedCell];
    }
};

DNADeletion
本题难度比通常的Division1 500要大。num[i]表示前i个字符构成的字符串一定要用上最后一个字符时的答案数目,初始时num[0]=1。每个氨基酸可能对应多个“三碱基”,而我们在递推时,要尽可能用前面的碱基,这样能使后面的碱基得到充分利用,得到更长的蛋白质,还可以避免重复。把所有的num值加在一起,去掉空蛋白质的情况就可得到正确结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cmath>
#include <ctime>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <climits>
#include <cstring>
#include <algorithm>
#include <vector>
 
using namespace std;
 
int next[3333][4],num[3333];
 
class DNADeletion
{
public:
    int differentProteins(vector <string> DNASequence, vector <string> codonTable)
    {
		const string S="AGCT";
		const int M=1000000007;
		string s,t,str;
		int n=codonTable.size();
		map <string,vector<vector<int> > > d;
		for(int i=0;i<DNASequence.size();++i) str+=DNASequence[i];
		for(int i=0;i<n;++i)
		{
			istringstream sin(codonTable[i]);
			sin>>s>>t;
			vector <int> m;
			for(int j=0;j<3;++j) m.push_back(S.find(s[j]));
			d[t].push_back(m);
		}
		int len=str.size();
		for(int i=0;i<4;++i) next[len][i]=-2;
		for(int i=len-1;i>=0;--i)
		{
			for(int j=0;j<4;++j) next[i][j]=next[i+1][j];
			next[i][S.find(str[i])]=i;
		}
		memset(num,0,sizeof(num));
		num[0]=1;
		for(int i=0;i<len;++i)
			for(map<string,vector<vector<int> > >::iterator it=d.begin();it!=d.end();++it)
			{
				int minP=INT_MAX;
				for(int j=0;j<it->second.size();++j)
				{
					int p=i;
					for(int k=0;k<3&&p>-1;++k) p=next[p][it->second[j][k]]+1;
					if(p>-1) minP=min(minP,p);
				}
				if(minP!=INT_MAX) num[minP]=(num[minP]+num[i])%M;
			}
		int ans=0;
		for(int i=1;i<=len;++i) ans=(ans+num[i])%M;
		return ans;
    }
};
Posted in SRM, TopCoder. Tags: , , . No Comments »

TopCoder SRM433 Summary

MagicWords
枚举+优化
如果直接枚举全排列,然后朴素判断拼接成的字符串是不是magicword会超时。如何优化呢?不难证明如下定理:如果一个长度为L的字符串s为magicword,那么s一定为K个相同子串拼接而成,并且s不能分割成i(i>K)个相同子串。这样,判断s是否为magicword时,我们需要先判断s是否由K个相同子串拼接而成。如果是,就枚举L的大于K的约数i,依次判断s是否由i个相同子串拼接而成,如果都不是,则s为magicword。L的约数个数大约为sqrt(n)个,时间复杂度约为O(n!*L*sqrt(L))。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <cmath>
#include <ctime>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <algorithm>
#include <vector>
 
using namespace std;
 
int g[111];
 
class MagicWords
{
public:
    int count(vector <string> S, int K)
    {
		int ans=0,n=S.size(),L=0,d[11];
		for(int i=0;i<n;++i) d[i]=i,L+=S[i].size();
		if(L%K) return 0;
		int len=0;
		for(int i=L;i>=K;--i)
			if(L%i==0)
				g[len++]=L/i;
		do
		{
			string s="";
			for(int i=0;i<n;++i) s+=S[d[i]];
			int i,j;
			for(i=0;i<len;++i)
			{
				for(j=g[i];j<L;++j)
					if(s[j]!=s[j%g[i]])
						break;
				if(j==L) break;
			}
			if(i==len-1) ++ans;
		} while(next_permutation(d,d+n));
		return ans;
    }
};

SettingTents
枚举+优化
本题解法分为两步:1、枚举出所有菱形的形状,确保不重不漏;2、对于每种形状的菱形,算出有多少个位置可以将其放进网格中。为枚举菱形的形状,我们先确定一个顶点,比如菱形最左边的顶点(如果菱形的左边是一条竖直的边就取下边的那个顶点),以它为原点(0,0),从这里开始引边。如果我们枚举到两条边对应的顶点分别为(x1,y1)和(x2,y2),则第四个顶点坐标为(x1+x2,y1+y2)。直接枚举会超时,但是注意到x1*x1+y1*y1==x2*x2+y2*y2,所以我们可以先将边长相同的所有(x,y)预处理到一个表中,这样计算时直接提取边长相同的边可以大大加快速度。最后,如果已确定菱形形状,找出这个菱形的“横平竖直”的外接矩形,设这个外接矩形长W宽H,则放置方案数为(N-W+1)*(M-H+1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cmath>
#include <ctime>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <algorithm>
#include <vector>
 
using namespace std;
 
int min(int a,int b,int c,int d)
{
	return min(min(min(a,b),c),d);
}
 
int max(int a,int b,int c,int d)
{
	return max(max(max(a,b),c),d);
}
 
class SettingTents
{
public:
	int countSites(int r,int c)
	{
		int ans=0;
		map <int,vector<pair<int,int> > > h; 
		for(int i=0;i<=r;++i)
			for(int j=-c;j<=c;++j)
				if(!(i==0&&j<=0))
					h[i*i+j*j].push_back(make_pair(i,j));
		for(int L=1;L<=r*r+c*c;++L)
			for(int i=0;i<h[L].size();++i)
				for(int j=i+1;j<h[L].size();++j)
				{
					int minx=min(0,h[L][i].first,h[L][j].first,h[L][i].first+h[L][j].first);
					int maxx=max(0,h[L][i].first,h[L][j].first,h[L][i].first+h[L][j].first);
					int miny=min(0,h[L][i].second,h[L][j].second,h[L][i].second+h[L][j].second);
					int maxy=max(0,h[L][i].second,h[L][j].second,h[L][i].second+h[L][j].second);
					int x=maxx-minx,y=maxy-miny;
					if(x>r||y>c) continue;
					ans+=(r-x+1)*(c-y+1);
				}
		return ans;
    }
};

BarbarianInvasion
网络流
将入侵部队看成源,首都看成汇,其它方格看成中间结点,相邻方格对应的结点间连边,这样构造出了一个网络。注意到本题的限制条件不在边上而在方格(也就是结点)上,用拆点解决这个问题,即将所有结点拆成两个结点并在其间连一条边,容量限制为破坏这个方格的花费,所有原来的边的容量都设为无穷大。我们希望删除尽可能少的容量小的边使得源到汇不连通,容易联想到最小割。这个网络的最小割能保证所有删除的边容量和最小,但是本题要求删除的边数最小,其次容量和最小,因此我们需要改造网络使得多删除一条边的代价很高:将所有方格的花费都加上一个非常大的整数即可。

Posted in SRM, TopCoder. Tags: , , . No Comments »