[GCJ2017R3]Cooclement

题目大意:
  一种数列按照如下方式变化:
  新数列第i位等于原数中数字i的出现次数。
  变化过程中数列长度不变。
  例如数列12的变化过程为12-11-20-01-10。
  现在告诉你一个数列x,请求出x可能是有几种数列变化而来的。

思路:
  将整个变化过程倒过来,除去自环就是一棵树。
  题目就变成了求子树的大小。
  显然枚举一个状态所有的前驱(即树上的子结点)会超时,只有25分。
  然而如果只是求出一个状态对应的后继(父结点)会很简单。
  我们可以枚举出所有的状态,然后求出其后继,最后拓扑排序时DP求出子树大小即可。
  对于一些不存在的状态(各位数之和大于长度),我们则没必要遍历,可以特判判掉。
  如果一个状态的所有前驱都是不存在的,我们可以直接用组合算出它前驱的数量。

  1 #include<queue>
  2 #include<cstdio>
  3 #include<cctype>
  4 #include<ext/hash_map>
  5 int n;
  6 inline int getint() {
  7     register char ch;
  8     n=1;
  9     while(!isdigit(ch=getchar()));
 10     register int x=ch^'0';
 11     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'),n++;
 12     return x;
 13 }
 14 const int N=10;
 15 const int pow[]={1,10,100,1000,10000,100000,1000000,10000000,100000000};
 16 __gnu_cxx::hash_map<int,int> size[N];
 17 inline int fact(const int &n) {
 18     int ret=1;
 19     for(register int i=1;i<=n;i++) {
 20         ret*=i;
 21     }
 22     return ret;
 23 }
 24 inline int comb(const int &n,const int &m) {
 25     return fact(n-m);
 26 }
 27 __gnu_cxx::hash_map<int,int> deg[N];
 28 __gnu_cxx::hash_map<int,int> hash_table[N];
 29 int cnt[N];
 30 int hash(const int &n,const int &x) {
 31     if(hash_table[n].count(x)) return hash_table[n][x];
 32     return hash_table[n][x]=++cnt[n];
 33 }
 34 int nxt[N][50000];
 35 inline void add_edge(const int &n,const int &u,const int &v) {
 36     nxt[n][hash(n,u)]=hash(n,v);
 37     deg[n][hash(n,v)]++;
 38 }
 39 void dfs2(const int now,const int &n) {
 40     int next=0,tmp=now;
 41     while(tmp) {
 42         next+=tmp%10?pow[n-tmp%10]:0;
 43         tmp/=10;
 44     }
 45     if(next==now) {
 46         size[n][now]--;
 47         return;
 48     }
 49     add_edge(n,now,next);
 50 }
 51 void dfs(const int cur,const int &n,const int now,const int sum) {
 52     if(sum>n) return;
 53     if(cur==n) {
 54         if(!sum||size[n].count(hash(n,now))) return;
 55         int &ans=size[n][hash(n,now)];
 56         int nn=n,num=now;
 57         ans=fact(nn);
 58         while(num) {
 59             ans/=fact(num%10);
 60             nn-=num%10;
 61             num/=10;
 62         }
 63         ans/=fact(nn);
 64         ans++;
 65         dfs2(now,n);
 66         return;
 67     }
 68     for(int i=0;i<=n;i++) {
 69         dfs(cur+1,n,(((now<<2)+now)<<1)+i,sum+i);
 70     }
 71 }
 72 int main() {
 73     int T=getint();
 74     for(register int i=1;i<=T;i++) {
 75         const int x=getint();
 76         int tmp=x,sum=0;
 77         while(tmp) {
 78             sum+=tmp%10;
 79             tmp/=10;
 80         }
 81         if(sum>n) {
 82             printf("Case #%d: %d\n",i,1);
 83             continue;
 84         }
 85         if(size[n].empty()) {
 86             dfs(0,n,0,0);
 87             std::queue<int> q;
 88             for(register int j=1;j<=cnt[n];j++) {
 89                 if(!deg[n][j]) {
 90                     q.push(j);
 91                 } else {
 92                     size[n][j]=1;
 93                 }
 94             }
 95             while(!q.empty()) {
 96                 const int x=q.front();
 97                 q.pop();
 98                 size[n][nxt[n][x]]+=size[n][x];
 99                 if(!--deg[n][nxt[n][x]]) q.push(nxt[n][x]);
100             }
101         } 
102         printf("Case #%d: %d\n",i,size[n][hash(n,x)]);
103     }
104     return 0;
105 }

 

posted @ 2017-10-08 15:18  skylee03  阅读(140)  评论(0编辑  收藏  举报