TopCoder SRM 572

div.1

T1

题意

有一个字符串 和一个数k
改变最少的字符使得字符串的前k个字符与后k个字符 相同

思路

使用并查集,将需要相同的字符并起来。再在每个集合中将其修改为出现次数最多的字符。

#include 
using namespace std;
int f[10000],vis[100010];
map<char,int>num;
class NewArenaPassword {
public:## 标题
    int minChange( string oldPassword, int K );
};
int NewArenaPassword::minChange(string a, int k) {
	int i,n,j,ans=0,x;
	n=a.size();
	memset(vis,0,sizeof(vis));
	memset(f,-1,sizeof(f));
	if(n==k) return 0;
    for(i=0;i<k;i++) f[i+n-k]=i;
    for(i=n;i>=0;i--)
    {
    	if(vis[i]==0)
    	{
    		num.clear();
    		x=0;
			int temp=0;
			j=i;
			while(j!=-1)
			{
				vis[j]=1;
				x++;
				num[a[j]]++;
				temp=max(temp,num[a[j]]);
				j=f[j];
			}
			ans+=(x-temp);
		}
    }
    return ans;
}

T2

题意

给出n个字符串组p 和 一个数组a
a[i]表示p[i]串中有a[i]个位置与正确的串相同
判断 输出 正确的串
如有多个答案 输出 Ambiguity
如没有答案 输出 Liar

思路

使用中途相遇法。
将数字拆成前后两半,枚举前一半,算出每次猜测的猜对位数,放入map中,再枚举后一半,算出互补的前一半猜对位数,在map中查询。

#include 
using namespace std;
class EllysBulls {
public:
    string getNumber( vector <string> guesses, vector <int> bulls );
};
inline string work(int a,int l)
{
	string s(l,'0');
	for(int i=1;a;i++)
	{
		s[l-i]=a%10+'0';
		a=a/10;
	}
	return s;
}
inline int sol(const string &x,const string &y)
{
	int ans=0,i;
	for(i=0;i<x.size();i++) ans+=(x[i]==y[i]);
	return ans;
}
string EllysBulls::getNumber(vector <string> a, vector <int> b) {
    int i,j,t=1,n,m,l,r,x;
    string s,ans="Liar";
    n=a.size();
    m=a[0].size();
    l=m>>1;
    r=m-l;
    vector<int> d(n);
    map<vector<int>,int> h;
	for(i=0;i<l;i++) t=t*10;
	for(i=0;i<t;i++)
	{
		s=work(i,l);
		for(j=0;j<n;j++)
			d[j]=sol(s,a[j].substr(0,l));
		if(h.count(d)) h[d]=-1;
		else h[d]=i;
	}
	t=1;
	for(i=1;i<=r;i++) t=t*10;
	for(i=0;i<t;i++)
	{
		s=work(i,r);
		for(j=0;j<n;j++)
			d[j]=b[j]-sol(s,a[j].substr(l));
		if(h.count(d))
		{
			x=h[d];
			if(x==-1||ans!="Liar") return "Ambiguity";
			ans=work(x,l)+s;
		}
	}
	return ans;
}

div.2

T3

题意

n=s[1]+s[2]+…+s[k];
s序列的每一个数对于m的余数不同
k可以为任意数
问有多少个s序列

思路:

有点类似0/1背包,每个剩余类只能选一个,可以用f[i][j]表示i个在[0,M−1]的数和为j的方案数,DP一波。
对于f[i][j],若j≡N(mod M)且j<=N,则对答案的贡献为
f[i][j]∗C((N−j)/M+i−1,i−1)∗i

考虑给每个数加上m的整数倍之后模m不会发生变化,所以肯定还是符合模m两两互不相同,然而和却神不知鬼不觉地增大了。所以要使总和为N就相当于把(N−j)/M个M分给i个数,套用经典的小球模型就是把(N−j)/M个小球分给i个盒子,可以为空,这个方案数是C((N−j)/M+i−1,i−1),题目里是有序的,所以还要乘个阶乘。

为方便计算,上式进一步简化就是f[i][j]∗A((N−j)/M+i−1,i−1)∗i

#include 
using namespace std;
const int mod=1e9+7;
long long d[100][2510];
class DistinctRemainders {
public:
    int howMany( long long N, int M );
};
long long sol(long long x,long long y)
{
	if(x<y) return 0;
	long long s=1;
	for(int i=1;i<=y;i++) 
		s=(s*((x-i+1)%mod))%mod;
	return s;
}
int DistinctRemainders::howMany(long long n, int m) {
    int i,tot=0,k;
    long long ans=0;
    d[0][0]=1;
    for(i=0;i<m;i++)
    {
    	tot+=i;
    	for(int j=i+1;j>0;j--)
    		for(k=i;k<=tot;k++)
    			d[j][k]=(d[j][k]+d[j-1][k-i])%mod;
    }
     for(i=1;i<=m;i++)
     for(long long j=n%m;j<=n&&j<=tot;j+=m)
     	ans=(ans+d[i][j]*sol((n-j)/m+i-1,i-1)%mod*i%mod)%mod;
    return (int)ans;
}

你可能感兴趣的:(TopCoder,题解)