题目大意:
有一组序列,询问能分成几组使得每一组中的序列都互成双射
做法:
- 将每组序列接成环
- 把每一个元素替换成上一次该元素出现的位置与现在的位置的距离
- 将新的序列做一遍最小表示法
- 将最小表示的序列加到set中
- 最后答案即为set的大小
代码:
12548936 | 2015-08-15 | 17:29:14 | NKWBTB | D - Different vectors | GNU C++ | Accepted | 310 ms | 900 KB |
---|
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <cstring>
#include <set>
#include <map>
#define MAXN 20001
using namespace std;
int n,s;
struct minr{
int v[110];
friend bool operator <(const minr &a,const minr &b)
{
for(int i=0;i<=s;i++)
if(a.v[i]!=b.v[i])
return a.v[i]<b.v[i];
return 0;
}
}node;
int num[MAXN],str[MAXN];
set<minr>se;
map<int,int>m;
int MinimumRepresentation(int *s, int l)
{
int i=0,j=1,k=0,t;
while(i<l && j<l && k<l)
{
t=s[(i+k)>=l?i+k-l:i+k]-s[(j+k)>=l?j+k-l:j+k];
if(!t) k++;
else
{
if(t>0)i=i+k+1;
else j=j+k+1;
if(i==j)++j;
k=0;
}
}
return (i<j?i:j);
}
//最小表示法
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&s);
for(int i=0;i<n;i++)
{
m.clear();
for(int j=0;j<s;j++)
{
scanf("%d",&num[j]);
if(m.find(num[j])==m.end())str[j]=0;
else str[j]=j-m[num[j]];
m[num[j]]=j;
}
for(int j=0;j<s;j++)
{
if(m[num[j]]>j)str[j]=s-m[num[j]]+j;
m[num[j]]=j;
}
int st=MinimumRepresentation(str,s);
for(int j=st;j<s;j++)node.v[j-st]=str[j];
for(int j=0;j<st;j++)node.v[s-st+j]=str[j];
se.insert(node);
}
printf("%d\n",(int)se.size());
se.clear();
}
return 0;
}