2012 MultiUniversity Training Contest 1

1001 Clairewd’s message

题目大意:第一行输入字母加密规则,第二行输入一个串a以及加密后的a’组成了一个串a’a。但是这个串残缺了一部分内容(a的部分残缺了),只剩下了s=a’b(b是a的一个前缀或者空串)

让求出最短的a。
View Code 
 1 #include <map>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 const int maxn=100001;
 6 using namespace std;
 7 int main()
 8 {
 9     char s[27],s1[maxn];
10     int l1,t,i,j,k,n,flag,l;
11     scanf("%d%*c",&t);
12     while(t--)
13     {
14         map<char,char>a;
15         gets(s);
16         for(i=0;i<26;i++)
17             a[s[i]]=i+'a';
18         gets(s1);
19         l1=strlen(s1);
20         if(l1&1)
21             k=l1/2+1;
22         else
23             k=l1/2;
24         l=l1;
25         for(i=k;i<l1;i++)
26         {
27             flag=0;
28             for(j=i;j<l1;j++)
29             {
30                 if(a[s1[j-i]]!=s1[j])
31                 {
32                     flag=1;
33                     break;
34                 }
35             }
36             if(!flag)
37             {
38                 l=i;
39                 break;
40             }
41         }
42         if((l1&1==0)&&l==(l1/2))
43             puts(s1);
44         else
45         {
46             j=0;
47             printf("%s",s1);
48             for(i=l1-l;i<l;i++)
49                 printf("%c",a[s1[i]]);
50             puts("");
51         }
52     }
53     return 0;
54


1002 Divide Chocolate
 题目大意:给一个n*2的矩形,问分成k部分共有多少种分法。

介个代码不是我写的。。。

View Code 
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int mod=100000007;
 6 __int64 dp[1001][2001][2];
 7 int main()
 8 {
 9     memset(dp,0,sizeof(dp));
10     dp[1][1][0]=dp[1][2][1]=1;
11     int i,j;
12     for(i=2;i<=1000;i++)
13     {
14         for(j=1;j<=2000;j++)
15         {
16             dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j-1][0]+dp[i-1][j][1]*2+dp[i-1][j-1][1];
17             dp[i][j][1]=dp[i-1][j-1][1]*2+dp[i-1][j][1]+dp[i-1][j-1][0]*2;
18             if(j>1)
19             {
20                 dp[i][j][1]+=dp[i-1][j-2][0]+dp[i-1][j-2][1];
21             }
22             dp[i][j][0]%=mod;
23             dp[i][j][1]%=mod;
24         }
25     }
26     int t,n,m;
27     scanf("%d",&t);
28     while(t--)
29     {
30         scanf("%d%d",&n,&m);
31         printf("%I64d\n",(dp[n][m][0]+dp[n][m][1])%mod);
32     }
33     return 0;
34

1003 Holedox Eating 

题目大意:有一个可以看成点的什么动物,初始在0位置,无朝向,然后它的前面后面会冒出食物来,它选最近的食物去吃,如果前后距离相等,它会选它朝向方向的食物。

比赛时这道题,看完,想到了用:链表,priority_queue,multiset,线段树……

priority_queue方法,281MS:

View Code 
 1 #include <iostream>
 2 #include <queue>
 3 #include <cstdio>
 4 using namespace std;
 5 struct cmp
 6 {
 7     bool operator()(int a, int b)
 8     {
 9         return a>b;
10     }
11 };
12 int main()
13 {
14     int i,t,a,b,c,n,m,x,y,sum,dir,k=0;
15     scanf("%d",&t);
16     while(t--)
17     {
18         k++;
19         y=0;
20         sum=0;
21         priority_queue<int> back;
22         priority_queue<int,vector<int>,cmp>front;
23         scanf("%d%d",&m,&n);
24         for(i=0;i<n;i++)
25         {
26             scanf("%d",&c);
27             if(!c)
28             {
29                 scanf("%d",&x);
30                 if(x>y)
31                     front.push(x);
32                 else
33                     back.push(x);
34             }
35             else
36             {
37                 a=b=-1;
38                 if(!back.empty()) 
39                     b=back.top();
40                 if(!front.empty()) 
41                     a=front.top();
42                 if(b==y)/*如果当前位置有直接删除*/
43                 {
44                     back.pop();
45                     continue;
46                 }
47                 if(a+1&&b+1)/*当前位置的前面和后面都有*/
48                 {
49                     if(a-y>y-b)/*前面位置比后面位置的距离大*/
50                     {
51                         sum+=y-b;
52                         y=b;
53                         back.pop();
54                         dir=0;
55                     }
56                     else if(a-y==y-b)/*如果距离相等*/
57                     {
58                         sum+=a-y;
59                         if(dir)/*朝向前面*/
60                         {
61                             y=a;front.pop();
62                         }
63                         else 
64                         {
65                             y=b; 
66                             back.pop();
67                         }
68                     }
69                     else
70                     {
71                         sum+=a-y;
72                         y=a;
73                         front.pop();
74                         dir=1;
75                     }
76                 }                
77                 else if(b+1)/*只有后面有*/
78                 {
79                     sum+=y-b;;
80                     y=b;
81                     back.pop();
82                     dir=0;
83                 }
84                 else if(a+1)/*只有前面有*/
85                 {
86                     sum+=a-y;
87                     y=a;
88                     front.pop();
89                     dir=1;
90                 }
91             }
92         }
93         printf("Case %d: %d\n",k,sum);
94     }
95     return 0;
96


线段树 方法,671MS:

View Code 
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 #include <algorithm>
  5 #define lson l,m,rt<<1
  6 #define rson m+1,r,rt<<1|1
  7 using namespace std;
  8 int sum[500001];
  9 void build(int l,int r,int rt)
 10 {
 11     sum[rt]=0;
 12     if(l==r)
 13         return;
 14     int m=(l+r)>>1;
 15     build(lson);
 16     build(rson);
 17 }
 18 void pushup(int rt)
 19 {
 20     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 21 }
 22 void update(int a,int add,int l,int r,int rt)
 23 {
 24     if(l==a&&r==a)
 25     {
 26         sum[rt]+=add;
 27         return;
 28     }
 29     int m=(l+r)>>1;
 30     if(a<=m)
 31         update(a,add,lson);
 32     else
 33         update(a,add,rson);
 34     pushup(rt);
 35 }
 36 int query1(int a,int b,int l,int r,int rt)
 37 {
 38     if(a<=l&&b>=r)
 39         return sum[rt];
 40     int m=(l+r)>>1,t=0;
 41     if(a<=m)
 42         t+=query1(a,b,lson);
 43     if(b>m)
 44         t+=query1(a,b,rson);
 45     return t;
 46 }
 47 int query2(int s,int l,int r,int rt)
 48 {
 49     if(l==r)
 50         return l;
 51     int m=(l+r)>>1;
 52     if(sum[rt<<1]>=s)
 53         return query2(s,lson);
 54     return query2(s-sum[rt<<1],rson);
 55 }
 56 int query3(int s,int l,int r,int rt)
 57 {
 58     if(l==r)
 59         return l;
 60     int m=(l+r)>>1;
 61     if(sum[rt<<1|1]>=s)
 62         return query3(s,rson);
 63     return query3(s-sum[rt<<1|1],lson);
 64 }
 65 int main()
 66 {
 67     int t,k=0,n,m,x,y,s,c,d,a1,a2,b1,b2;
 68     scanf("%d",&t);
 69     while(t--)
 70     {
 71         k++;
 72         x=1;
 73         y=0;
 74         s=0;
 75         scanf("%d%d",&m,&n);
 76         build(0,m,1);
 77         while(n--)
 78         {
 79             scanf("%d",&c);
 80             if(c==0)
 81             {
 82                 scanf("%d",&d);
 83                 update(d,1,0,m,1);
 84             }
 85             else
 86             {
 87                 if(sum[1]==0)
 88                     continue;
 89                 a1=query1(0,y,0,m,1);
 90                 a2=query1(y,m,0,m,1);
 91                 if(a1==0)
 92                 {
 93                     b2=query3(a2,0,m,1);
 94                     s+=b2-y;
 95                     y=b2;
 96                     x=1;
 97                     update(y,-1,0,m,1);
 98                 }
 99                 else if(a2==0)
100                 {
101                     b1=query2(a1,0,m,1);
102                     s+=y-b1;
103                     y=b1;
104                     x=0;
105                     update(y,-1,0,m,1);
106                 }
107                 else
108                 {
109                     b1=query2(a1,0,m,1);
110                     b2=query3(a2,0,m,1);
111                     if(y-b1<b2-y)
112                     {
113                         s+=y-b1;
114                         y=b1;
115                         x=0;
116                         update(y,-1,0,m,1);
117                     }
118                     else if(y-b1>b2-y)
119                     {
120                         s+=b2-y;
121                         y=b2;
122                         x=1;
123                         update(y,-1,0,m,1);
124                     }
125                     else
126                     {
127                         s+=b2-y;
128                         if(x==1)
129                         {
130                             y=b2;
131                             update(y,-1,0,m,1);
132                         }
133                         else
134                         {
135                             y=b1;
136                             update(y,-1,0,m,1);
137                         }
138                     }
139                 }
140             }
141         }
142         printf("Case %d: %d\n",k,s);
143     }
144     return 0;
145

其他题:待续、、、

附:

官方题解: 

1001  Clairewd's message

这道题问的就是将1个串如何变为stringA+stringB的形式,使得stringA是stringB经过映射得到相同的串。映射那步其实没有什么价值,假设str为原串s经过映射后得到的串,我们可以以str为模式串,以s为原串做一次扩展KMP,得到extend数组,extend[i]表示原串以第i开始与模式串的前缀的最长匹配。经过O(n)的枚举,我们可以得到,若extend[i]+i=len且i>=extend[i]时,表示stringB即为该点之前的串,stringA即为该点之前的str串,最后输出即可。

1002  Divide Chocolate

题意:

给定一个2*n的矩形,求把这个矩形分割为k部分的方法,且对称的切割方法视为不同,输出时模上100000007。

(1<=n<=1000,1<=k<=2*n)

 

解法:

看到这个题目,很容易想到DP。

状态表示 f[i][0][j]:前i行已经出现了j部分且第i行的两个格子属于同一部分的方法数

         f[i][1][j]:前i行已经出现了j部分且第i行的两个格子属于不同部分的方法数

初始条件 f[1][0][1]=f[1][1][2]=1

状态转移 f[i+1][0][j]=(f[i+1][0][j]+f[i][0][j]+f[i][1][j]*2)%mod;

         f[i+1][0][j+1]=(f[i+1][0][j+1]+f[i][0][j]+f[i][1][j])%mod;

         f[i+1][1][j]=(f[i+1][1][j]+f[i][1][j])%mod;

         f[i+1][1][j+1]=(f[i+1][1][j+1]+f[i][0][j]*2+f[i][1][j]*2)%mod;

         f[i+1][1][j+2]=(f[i+1][1][j+2]+f[i][0][j]+f[i][1][j])%mod;

 

共12种不同的状态转移(见下图)

 

1003  Holedox Eating

每次吃蛋糕的时候,都要找离当前位置最近的蛋糕。可以利用线段树求区间[0, now]
已经存在的蛋糕的最大位置,,[now,L]已经存在的蛋糕的最小位置。按照输入顺序模拟就可以了。

1004  Hourai Jeweled


一次dfs就能搞定的问题,其实没什么复杂的算法,主要难点在于统计时的一些小技巧,个人难度评价是偏难的中档题。
从任意一点开始深搜,每颗子树搜索完毕之后向上返回pair<可以延伸到该点且最后一条边与由父节点到该点的边颜色不同的gorgeous边的条数 , 所有这种边分数的总和>
每次深搜完一个子节点之后,增加的过这一点的gorgeous边的总分数为:
    之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的分数 +
    之前深搜的所有子节点向上返回的分数之和 * 当前子节点返回的边数 +
    之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的边数 * 当前点的权

用O(n)的时间把所有的累计起来就好了什么的

才没有呢 = =
如果有当前节点分别到两个子节点的边的颜色相同的话也是不行的,
由于数据中添加了度数很大的点,理论上O(儿子数^2)的两两统计也是要被卡掉的 (希望确实的做到了
正确的做法是对所有的儿子按边的颜色排个序,然后在按这个序深搜和统计

1005  Let's Hit Our Head

这个问题等价于把N因数分解,不能有1
然后将序列交替组合的方案数算出来就可以了

比如6=2*3
我们有[6],[2,3],[3,2]
交替组合有
A6B6 B6A6
A2B6A3 A3B6A2 B2A6B3 B3A6B2
A2B2A3B3 A2B3A3B2 A3B3A2B2 A3B2A2B3
B2A2B3A3 B2A3B3A2 B3A3B2A2 B3A2B2A3
共14种

再比如8=2*2*2=2*4
我们有[8],[2,4],[4,2],[2,2,2]
交替组合有
A8B8 B8A8
A2B8A4 A4B8A2 B2A8B4 B4A8B2
A2B2A4B4 A2B4A4B2 A4B2A2B4 A4B4A2B2
B2A2B4A4 B2A4B4A2 B4A2B2A4 B4A4B2A2
A2B2A2B4A2 A2B4A2B2A2 B2A2B2A4B2 B2A4B2A2B2
A2B2A2B2A2B2 B2A2B2A2B2A2
共20种

1006  Lightning

题目模型:给定平面上N个点。如果两点距离小于等于R,且两点间线段上没有其他点的时候,两点可以建立一条边。得到这个图后,求此图的生成树个数 mod 10007,如果图不连通则输出-1.
第一部分:构图。枚举两点是否符合距离限制,如果符合则对比此两点方向向量上是否有距离更小的其他点,然后根据情况建边删边。(O(N*N*log(N)))
第二部分:如果图连通,则根据生成树定理,从建好的图中构建M矩阵,高斯消元法求出行列式的值(由于是整数高消,需要提出系数,最后求其逆元)(O(N^3))

1007  Mahjong

这道题目需要用2-sat解决,但要成功转化出模型需要一些巧妙的构造和基础图论知识的证明,最后还要注意一些细节。

首先,题目中的前三段可以直接无视(偶已经很体贴的用图隔开了不是么)。。

要解决这道题目,我们首先可以构造证明,一个状态经过最多三段操作(每段操作指若干次某种操作)之后可以到达任意状态。
我们不妨假设操作顺序为A...AB...BA...A(反之类似)。
原图中的一个格子坐标为(x0,y0),要转移到目标的坐标为(x1,y1),则显然最后一段操作之前坐标为(x1,y2).
依次类推,它的转移路径应该为:(x0,y0)--->(x0,y2)--->(x1,y2)--->(x1,y1),则唯一影响路径的是y2。
那么这种构造方式能否成功就取决于从(x0,y2)是否能转换到(x1,y2)。
换言之,就是我们第一段操作需要使每一列各点所要到达的x1互不相等。
再换言之,如果我们以每个点的原始位置集x0到其目标位置集x1构建二分图,则如果能够找到n个不相交的完美匹配就构造成功了。
显然每行有n个点,则x集每个点的度都是n;同理y集每个点的度也是n。
对于x集的任意子集S,其在y集中的相邻集记作A(S),显然S的度为n|S|,而其邻集A(S)的度至少为n|S|。
由于每个点的度数都相同为n,则显然|A(S)|>=|S|,该图一定存在完美匹配。
将该匹配删掉之后,每个点的度数依然相等,同理可证我们可以找到下一组匹配。则我们可以证明上文的结论。

有了上面的证明,我们可以根据情况进行分类讨论。
1.不能达到
2.直接等同
3.一步可达
4.两步可达
5.三步可达

前三种都可以直接判断,后两种我们只要可以判断一种就知道不满足其他四种的就是最后一种。注意每种判断都需要行列各做一次。
我们考虑怎么判断两步可达的状态,不妨先考虑A...AB...B的操作(反之同理)。
每个点(x0,y0)要到达目标点(x1,y1),显然路径为(x0,y0)--->(x0,y1)--->(x1,y1)。
由于每种点最多出现两次,则每种点可选的中转点方案最多两组。
当某两种点的某种方案需要的中转点之间出现交集的时候,说明二者方案不能同时选择。
那么这是经典的2-sat问题,可以用强联通分量或者染色解决。

具体的实现上应该有一些技巧,偶的标程实现方法过于naive了,跑了2s....QAQ。

1008  Matrix

化简一下,得到

 

这里就可以勉强看得出是一个最大权闭合子图的模型了。但是可以继续化简

 

注意到第一项是定值,那么也就是要最小化后面的括号内的三项之和。这个就比较容易抽象到最小割模型了。

建图,令源为S,汇为T,中间有N个点。点i到j有一条容量为B[i][j]的边,同时S到点i有一条容量为C[i]的边,点i到汇点有一条容量为sum{j}B[i][j]的边。这样,图的任意一个割就和一个A一一对应,就能确定后面三项之和。于是最小割即为最小的后面三项之和。

 

本题的基础模型是最大权闭合子图,但是套上一个数学公式,就不太容易看出来。

1009  Saving Princess claire

简单最短路。
给出的地图中,Y为起点,C为终点,#点不能通过,可直接忽略。所有的P为互通的传送门,故可将所以的P看作同一个点。每个能通过的点可以向上下左右四个方向走,如果对应的方向可以通过,则连边,若要走到的点是*,则边权为通过的费用,否则边权为0。
连边后求Y到C的最短路即可。

1010  Seikimatsu Occult Tonneru

先不考虑可以修复的桥的性质,则可以将模型简化为n个点的人通过有通过人数上限的有向边,到达一些有人数上限的特殊的边(隧道)。
可以建立最大流模型来求解,增加一个源点S,和一个汇点T。S向每个有人的点,连一条容量为人数的边,图中普通的u->v的有向边,连一条u->v的流量为无穷的边,桥的流量则为1。对于隧道,每个隧道可以虚拟出一个点,如u->v的隧道,可以虚拟一个点x,连接u->x,x->v的流量无穷的边,和x->T的流量为隧道人数上限的边,求解最大流即可得到最大人数。
现在考虑桥的问题,题目中说明了桥最多只有12座,故可以2^12枚举修复哪些桥,不修复的桥没有花费,连接的边流量为1,要修复的桥则计算花费,边的流量为无穷,这样进行2^12次最大流就可以得到最优解。

原文地址:https://www.cnblogs.com/pony1993/p/2600650.html