SRM 477 DIV1 1000

题意:给定一棵树,最多可给这棵树增加k条捷径,每条捷径长度为L,且只能走一次,问从0出发回到0遍历所有的边需要走的最短距离。

树型dp,状态为dp[i][j][2]。
dp[i][j][0]表示从i出发,用了j次捷径,遍历完下面所有边且最后走的路径到底后没有回来的最小距离。
dp[i][j][1]表示从i出发,用了j次捷径,遍历完下面所有边且回到i的最小距离。
转移看代码:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <cstdio>
#include <iostream>
#include <cstring>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#define clr(x,y) memset(x,y,sizeof(x))
#define Min(x,y) if(y<x) x=y
#define Max(x,y) if(y>x) x=y
using namespace std ;
typedef long long ll ;
const double eps = 1e-6 ;

const int inf = 1e8;
vector<int> ev[205],ew[205];
int dp[205][105][2],p[105][2];
string s = "";
int K,L;

class KingdomTour
{
public:

void dfs(int u,int f){
for(int i=0;i<=K;i++) dp[u][i][0] = dp[u][i][1] = 0;

for(int i=0;i<(int)ev[u].size();i++){
int v = ev[u][i];
int w = ew[u][i];
if(v==f) continue;
dfs(v,u);

for(int i=0;i<=K;i++) p[i][0] = p[i][1] = inf;
for(int i=0;i<=K;i++)
for(int j=0;j<=K;j++)
if(i+j<=K){
Min(p[i+j+1][0],dp[u][i][0]+dp[v][j][0]+L+w);
//之前有儿子节点不回来, v最后通过捷径回到u
Min(p[i+j][0],dp[u][i][0]+dp[v][j][1]+2*w);
//之前有儿子节点不回来, v直接走回到u
Min(p[i+j][0],dp[u][i][1]+dp[v][j][0]+w);
//之前没有儿子节点不回来,v不回到u
Min(p[i+j][1],dp[u][i][1]+dp[v][j][1]+2*w);
//之前没有儿子节点不回来,v直接走回到u
Min(p[i+j+1][1],dp[u][i][1]+dp[v][j][0]+w+L);
//之前没有儿子节点不回来,v最后通过捷径回到u
Min(p[i+j+1][1],dp[u][i][0] + dp[v][j][0]+L+w);
//之前有儿子节点不回来,直接走到当前儿子叶子节点走回来。
}

for(int i=0;i<=K;i++){
dp[u][i][0] = p[i][0];
dp[u][i][1] = p[i][1];
}
}
}
void get(int &w,int &i){
w = 0;
while(i<(int)s.size() && s[i]<='9' && s[i]>='0'){
w = w*10 +s[i]-'0';
i++;
}
i++;
}
int minTime(int N, vector <string> roads, int K, int L){
::K = K;
::L = L;
s = "";
for(int i=0;i<(int)roads.size();i++) s += roads[i];
for(int i=0;i<N;i++) ev[i].clear(),ew[i].clear();
int l = 0;
while(1){
int u,v,w;
get(u,l);
get(v,l);
get(w,l);
ev[u].push_back(v);ew[u].push_back(w);
ev[v].push_back(u);ew[v].push_back(w);
if(l>=(int)s.size()) break;
}
dfs(0,0);
int ans = inf;
for(int i=0;i<=K;i++) ans = min(ans,dp[0][i][1]);
return ans;
}



};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor

Mehr lesen

TopCoder SRM 474 DIV1 1000

题意:把一棵树对应到一张无向图上有多少种方案,即满足树上两点之间有边,那图上对应的两点之间也有边的方案。N<=14

只要想到这是树型dp,这题就不难,状态为dp[i][j][mask],表示到树上节点为i对应图上节点为j时它及子树已经用过图上节点mask的方案数。mask是状态压缩。
转移的时候尽量优化,否则会tle。

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <cstdio>
#include <iostream>
#include <cstring>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#define INC(x,y) x = (x+y)%mod;
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std ;
typedef long long ll ;
const double eps = 1e-6 ;

const ll mod = 1000000007;
int dp[15][15][1<<14];
int p[15][1<<14];
int g[20][20],t[20][20];
int n;

class GameWithGraphAndTree
{
public:
void dfs(int u,int f){
int fb = 0;
for(int v=0;v<n;v++){
if(!t[u][v] || v==f) continue;
dfs(v,u);
if(fb == 0){
fb = 1;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(g[i][j])
for(int k=0;k<(1<<n);k++)
if(((k>>i)&1)==0){
INC(dp[u][i][k|(1<<i)],dp[v][j][k]);
}
}
else{
clr(p,0);
for(int i=0;i<n;i++)
for(int k=0;k<(1<<n);k++)
if(((k>>i)&1) && dp[u][i][k])
for(int j=0;j<n;j++)
if(g[i][j]){
int rt = (1<<n)-1-k;
for(int l=rt;l>0;l=(l-1)&rt){
if((l>>j)&1){
INC(p[i][k|l] , dp[u][i][k] * dp[v][j][l]);
}
}
}
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++) dp[u][i][j] = p[i][j];
}
}
if(fb==0){
for(int i=0;i<n;i++) dp[u][i][1<<i] = 1;
}
}
int calc(vector <string> g, vector <string> t){
n = g.size();
clr(dp,0);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(g[i][j]=='Y') ::g[i][j] = 1;
else ::g[i][j] = 0;
if(t[i][j]=='Y') ::t[i][j] = 1;
else ::t[i][j] = 0;
}
dfs(0,0);
int ans = 0;
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++) ans = (ans+dp[0][i][j])%mod;
return ans;
}



};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor

Mehr lesen

POJ 3415 Common Substrings

题意:求两个串中相同的长度大于K的子串对数。

两个串链接起来做后缀数组。
假设height数组为:
0 2 1 4 3 2 4
0 0 0 1 0 0 1
0表示是第一个串的后缀,1表示是第二个串的后缀。
如果现在求第7个后缀,能与之前第一个串的后缀得到多少的长度大于k的子串对数。
可以发现[1,2]区间的后缀和第7个后缀最长匹配都为1,[3,5]区间的后缀最长匹配为2,[6,6]区间的最长匹配为3, 因此用单调栈找到前一个比他小的height,那么这段区间的每个后缀都能得到 heigth-k+1个与第7个后缀长度大于等于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
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <cmath>
#include <algorithm>
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;

const int inf = 100000000;
const int N = 1000010;
int ua[N], ub[N], us[N];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m) //da(r, sa, n + 1, 256);(r[n] = 0)
{
int i,j,p,*x=ua,*y=ub,*t;
//r[]存放原字符串,且从char变为int
for(i=0; i<m; i++) us[i]=0; //sa[i]表示排名为i的后缀起始下标(i>=1,sa[i]>=0)
for(i=0; i<n; i++) us[x[i]=r[i]]++;
for(i=1; i<m; i++) us[i]+=us[i-1];
for(i=n-1; i>=0; i--) sa[--us[x[i]]]=i;
for(j=1,p=1; p<n; j*=2,m=p)
{
for(p=0,i=n-j; i<n; i++) y[p++]=i;
for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0; i<m; i++) us[i]=0;
for(i=0; i<n; i++) us[x[i]]++;
for(i=1; i<m; i++) us[i]+=us[i-1];
for(i=n-1; i>=0; i--) sa[--us[x[y[i]]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
int rank[N],height[N]; //height[i]为排第i-1和第i的后缀的公共前缀长度
void calheight(int *r,int *sa,int n)
{
int i,j,k=0;
for(i=1; i<=n; i++) rank[sa[i]]=i;
for(i=0; i<n; height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
}

int n,m,k;
int a[N],sa[N],st[N],top;
ll sum[N];
char s[N];


ll gao(int height[]){
for(int i=1;i<=n+m+1;i++)
if(sa[i] < n)
sum[i] = sum[i-1]+1;
else
sum[i] = sum[i-1];
ll ans = 0;
ll val = 0;
top = 0;
for(int i=1;i<=n+m+1;i++){
while(top>0 && height[i] < height[st[top]]){
int x = st[top] , y = st[top-1];
if(height[x]-k>=0){
if(y>0)
val -= (sum[x-1]-sum[y-1])*(height[x]-k+1);
else
val -= sum[x-1]*(height[x]-k+1);
}
top--;
}
int x = i,y = st[top];
if(height[x]-k>=0)
if(y>0)
val += (sum[x-1]-sum[y-1])*(height[x]-k+1);
else
val += sum[x-1]*(height[x]-k+1);
st[++top] = i;
if(sa[i]>n)
ans += val;
}
return ans;
}
int main(){
// freopen("/home/zyc/Documents/Code/cpp/in","r",stdin);
while(1){
scanf("%d",&k);
if(k==0) break;
scanf("%s",s);
n = strlen(s);
for(int i=0;i<n;i++) a[i] = s[i];
a[n] = 1;
scanf("%s",s);
m = strlen(s);
for( int i=0;i<m;i++) a[i+n+1] = s[i];
a[n+m+1] = 0;
da(a,sa,n+m+2,256);
calheight(a,sa,n+m+1);
ll ans = 0;
ans += gao(height);

for(int i=1;i<=n+m+1;i++) st[i] = sa[n+m+1-i+1];
for(int i=1;i<=n+m+1;i++) sa[i] = st[i];

for(int i=2;i<=n+m+1;i++) st[i] = height[n+m+1-i+2];
for(int i=2;i<=n+m+1;i++) height[i] = st[i];

ans += gao(height);
cout<<ans<<endl;
}

return 0;
}

Mehr lesen

HDU 4654 k-edge connected components

题意:求一个无向图中,k联通分量的个数。

迭代过程:
用Stoer_Wagner求出当前全局最小割,判断是否大于k,是就返回,不是就按割边把图分成两个部分继续迭代。
用Soter_Wagner按最小割把图分为两部分的方法:此算法一直把点合并,当合并到某一状态时,当前最小割被更新了,那么对于当前最小割 ,”prim”时最后加入的那个点及和他合并的点就是一部分,剩余的点就是另一部分,得到最小割时划分的两部分即可。

Mehr lesen

suffix automation

hdu 4622
题意:询问每个子串里包含的不同的子串的个数。
枚举每一个点作为起点做后缀自动机,维护自动机每个节点能有多少条路径到达。

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
#include <set>
#include <map>
#include <queue>
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
#define mpr(a,b) make_pair(a,b)
using namespace std;

const int ch = 26;
const int N=100000;
class SAM{
public:
int f[N],chd[N][ch+1],len[N],sw[200];
//ch+1 number of tempt suffix at status
int last,sz;
void init(){
clr(chd[0],0);
f[0] = -1;
chd[0][ch] = 1;
last = 0;
sz = 0;
for(int i=0;i<26;i++)
sw[i+'a'] = i;
}
int add(char inc,int l){
int ans = 0;
int c = sw[inc];
int x = last;
last = ++sz;
clr(chd[sz],0);
len[sz] = l;
for(;x!=-1&&chd[x][c]==0;x=f[x]){
chd[x][c] = sz;
chd[last][ch] += chd[x][ch];
ans += chd[x][ch];
}
if(x == -1){
f[sz]=0;
}
else{
int y = chd[x][c];
if(len[y] == len[x]+1){
f[sz]=y;
}
else{
sz++;
len[sz] = len[x] + 1;

memcpy(chd[sz],chd[y],ch*4);
// for(int i=0;i<ch;i++) chd[sz][i] = chd[y][i];
chd[sz][ch] = 0;

f[sz] = f[y];
f[last] = f[y] = sz;

for(;x!=-1&&chd[x][c]==y;x=f[x]){
chd[x][c] = sz;
chd[y][ch] -= chd[x][ch];
chd[sz][ch] += chd[x][ch];
}
}
}
return ans;
}
}sam;

char s[100000];
int n,m;


int g[2005][2005];
int main(){
// freopen("/home/zyc/Documents/Code/cpp/in","r",stdin);
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s);
n = strlen(s);
for(int i=0;i<n;i++){
sam.init();
for(int j=i;j<n;j++){
if(j!=i)
g[i][j] = g[i][j-1] + sam.add(s[j],j-i+1);
else
g[i][j] = sam.add(s[j],j-i+1);
}
}
int Q;
scanf("%d",&Q);
while(Q--){
int l,r;
scanf("%d%d",&l,&r);
l--;r--;
printf("%d\n",g[l][r]);
}
}
return 0;
}

Mehr lesen

HDU 4622 Reincarnation

题意: 长度为2000的串,10000个询问:[l,r]这个子串包含多少不同的子串。

比赛时用的后缀数组o(nQ) ,赛后用的hash o(nn*log(n)) 和后缀自动机,这个log的出现是因为hash写不来 = = .

后缀数组解发:
先对原来的串做一遍后缀数组,对于每个询问,对sa数组进行扫描,如果当前前缀在[l,r]范围内,就找到之前和他公共前缀最长且也在[l,r]范围内的串,lcp最长公共前缀,答案加上 串在当前询问中的长度减去最长公共前缀。
值得注意的是和当前串公共前缀最长的并不就是上一次在[l,r]范围内的串,也有可能是再之前,原因是询问的区间,相当于给每个串截断了一部分,所以区间里sa数组不再代表这区间里的串的排名,处理比较容易,见代码

Mehr lesen

Codeforces 316G3Good Substrings (30 points)

题意:给一个长为50000的字符串t,还有10个约束(p,l,r), 当某一字符串s在p中出现的次数大于等于l,小于等于r时满足约束,问t有多少个不同的子串满足所有约束。

先考虑t的子串的性质:
t 的长度为len
假设以 x 为起点的子串,终点在[l,len]范围内时满足所有约束条件中的小于部分。
那么对于以x+1为起点的子串,终点在[l1,len]范围内时满足所有约束条件中的小于部分
则有 l1 > l ,所以用 two point 先行扫描出l[0]~l[len-1] 。其中扫描过程中如何判断某一子串在约束串中出现的次数,用二分+后缀数组。
同样处理出 r[0]~r[len-1] 满足范围内时满足所有约束条件中的大于部分
那么对于x为起点的串,终点在l[x]~r[x]之间时满足所有约束条件。
还没结束,还需要判重,后缀数组再次登程。
我们需要找到以x为起点的串,在(0,x-1)为起点的串中出现过的次数,转换一下可以变成求x为起点的串与之前串最多能匹配到多长,记录为repl[x],从前从后扫各一遍sa数组,用树状数组和后缀数组的lcp来搞。
答案就是对 r[i]-max(repl[i],l[i])+1 (0<=i<len) 求和。

Mehr lesen

URAL 1956 Fire Signals

题意:
求一条直线满足,所有平面上点到这条直线的距离之和最小,点数n<1000,输出最小的距离纸盒之和。

经过长时间的YY,得出经过平面上的两个点的直线包含最优解。可以由 dis = |ax+by+c|/sqrt(aa+bb) 得到。
因为在直线一侧的点 dis 的正负号是相同的,可以利用这一点。先枚举一点,另外的点按到这点的角度排序,然后扫描枚举另一点,维护两边的点的x之和,y之和,用abs(sum(ax0+by0+c) -abs(ax1+by1+c))/sqrt(aa+bb) 跟新答案,x0,y0是直线一边点的x,y的和,x1,y1是另一边的。

Mehr lesen

A Contest to Celebrate Girlfriend's Birthday by Staginner

####Cut the rope II

题意:把长度为L的线段进行分割,分割后每一段都不相同的方案数。L<=50000

dp比较赞,可以发现因为每一段都不相同所以 至多会有 x段,x*(x+1)/2 = L。x最大值在320左右。
dp[i][j][k] i<=320,j<=50000 k<2
k=0 表示现有i段用掉j的长度时不存在长度为1的段的方案数。
k=1 表示现有i段用掉j的长度时存在长度为1的段的方案数。

Mehr lesen

Codeforces MemSQL start[c]up Round 1

####A Square and Rectangles

题意:给定5个不重叠的长方形,问是否组合成一个正方形。x,y范围10^5

先得到x,y的最大最小值
然后在这个范围内枚举 x ,判断每一个x的位置y是否完全覆盖,由于不相交,直接把包含这个x的长方形的y差值相加,判断是否和y最大值和最小值的差值相等。
trick: 漏判 x和y最大值和最小值的差值是否相等。

Mehr lesen