专题杂选

专题杂选

线段树专题

参考ACM大牛总结的线段树专辑

匈牙利算法+km算法

二分图最大匹配
二分图最大权完美匹配

HDU 1150(匈牙利算法)

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 110
using namespace std;
int n,m,k;
int g[maxn][maxn],linker[maxn];
bool used[maxn];
bool dfs(int u)
{
for(int i=0;i<m;i++)
{
if(g[u][i]&&!used[i])
{
used[i]=true;
if(linker[i]==-1||dfs(linker[i]))
{
linker[i]=u;
return true;
}
}
}
return false;
}
int hungary()
{
int ans=0;
memset(linker,-1,sizeof(linker));
for(int i=0;i<n;i++)
{
memset(used,0,sizeof(used));
if(dfs(i)) ans++;
}
return ans;
}
int main()
{
/*freopen("input.txt","r",stdin);*/
while(scanf("%d",&n),n)
{
scanf("%d%d",&m,&k);
memset(g,0,sizeof(g));
while(k--)
{
int i,u,v;
scanf("%d%d%d",&i,&u,&v);
if(u>0&&v>0) g[u][v]=1;
}
printf("%d\n",hungary());
}
return 0;
}

HDU 2255(km模版)

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
#include<cstdio>
#include<algorithm>
#include<cstring>
#define INT 0x3f3f3f3f
#define maxn 310
using namespace std;
int nx,ny;
int g[maxn][maxn];
int linker[maxn],lx[maxn],ly[maxn];
int slack[maxn];
bool visx[maxn],visy[maxn];
bool dfs(int x)
{
visx[x]=true;
for(int y=0;y<ny;y++)
{
if(visy[y]) continue;
int tmp=lx[x]+ly[y]-g[x][y];
if(tmp==0)
{
visy[y]=true;
if(linker[y]==-1||dfs(linker[y]))
{
linker[y]=x;
return true;
}
}
else
slack[y]=min(slack[y],tmp);
}
return false;
}
int km()
{
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly));
for(int i=0;i<nx;i++)
{
lx[i]=g[i][0];
for(int j=1;j<ny;j++)
lx[i]=max(lx[i],g[i][j]);
}
for(int i=0;i<nx;i++)
{
fill(slack,slack+ny,INT);
while(1)
{
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(dfs(i)) break;
int d=INT;
for(int j=0;j<ny;j++)
if(!visy[j])
d=min(d,slack[j]);
for(int j=0;j<nx;j++)
if(visx[j])
lx[j]-=d;
for(int j=0;j<ny;j++)
{
if(visy[j]) ly[j]+=d;
else slack[j]-=d;
}
}
}
int ans=0;
for(int i=0;i<ny;i++)
if(linker[i]!=-1)
ans+=g[linker[i]][i];
return ans;
}
int main()
{
/*freopen("input.txt","r",stdin);*/
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&g[i][j]);
nx=ny=n;
printf("%d\n",km());
}
return 0;
}

HDU 1533(km)

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
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define INT 0x3f3f3f3f
#define maxn 105
using namespace std;
int nx,ny;
int g[maxn][maxn];
int linker[maxn],lx[maxn],ly[maxn];
int slack[maxn];
bool visx[maxn],visy[maxn];
char map[maxn][maxn];
struct node
{
int x,y;
}house[maxn],man[maxn];
bool dfs(int x)
{
visx[x]=true;
for(int y=0;y<ny;y++)
{
if(visy[y]) continue;
int tmp=lx[x]+ly[y]-g[x][y];
if(tmp==0)
{
visy[y]=true;
if(linker[y]==-1||dfs(linker[y]))
{
linker[y]=x;
return true;
}
}
else
slack[y]=min(slack[y],tmp);
}
return false;
}
int km()
{
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly));
for(int i=0;i<nx;i++)
{
lx[i]=g[i][0];
for(int j=1;j<ny;j++)
lx[i]=max(lx[i],g[i][j]);
}
for(int i=0;i<nx;i++)
{
fill(slack,slack+ny,INT);
while(1)
{
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(dfs(i)) break;
int d=INT;
for(int j=0;j<ny;j++)
if(!visy[j])
d=min(d,slack[j]);
for(int j=0;j<nx;j++)
if(visx[j])
lx[j]-=d;
for(int j=0;j<ny;j++)
{
if(visy[j]) ly[j]+=d;
else slack[j]-=d;
}
}
}
int ans=0;
for(int i=0;i<ny;i++)
if(linker[i]!=-1)
ans+=g[linker[i]][i];
return ans;
}
int main()
{
/*freopen("input.txt","r",stdin);*/
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n==0 && m==0 ) break;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>map[i][j];
int cnt1=0,cnt2=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(map[i][j]=='m')
man[cnt1].x=i,man[cnt1++].y=j;
if(map[i][j]=='H')
house[cnt2].x=i,house[cnt2++].y=j;
}
for(int i=0;i<cnt1;i++)
for(int j=0;j<cnt2;j++)
{
g[i][j]=abs(house[i].x-man[j].x)+abs(house[i].y-man[j].y);
g[i][j]=-g[i][j];
}
nx=ny=cnt1;
printf("%d\n",-km());
}
return 0;
}

哈密顿回路

对比

哈密顿路径:由起点前往指定的终点 途中经过所有节点且只经过一次
哈密度回路:恰好经过所有点且最后回到起点的图
欧拉通路:通过图中每条边且只通过一次,并且经过每一顶点的通路
欧拉回路:通过图中每条边且只通过一次,并且经过每一顶点的回路
哈密顿:点 欧拉:边

cf 10D(求哈密顿回路的个数)

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool g[20][20];
ll dp[1<<20][20];
int n, m;
int main()
{
cin>>n>>m;
memset(g, false, sizeof(g));
while(m--)
{
int a, b;
cin>>a>>b;
g[a-1][b-1]=g[b-1][a-1]=true;
}
ll ans=0;
memset(dp, 0, sizeof(dp));
for(int i=0;i<n;i++)dp[1<<i][i]=1;
for(int i=0;i<(1<<n);i++)
{
for(int j=0;j<n;j++)
{
if(!dp[i][j]) continue;
int st=0;
for(int k=0;k<n;k++)
{
if(i&(1<<k))
{
st=k;
break;
}
}
if(g[st][j]&&__builtin_popcount(i)>2)
ans+=dp[i][j];
for(int k=st+1;k<n;k++)
{
if(!(i&(1<<k))&&g[j][k])
dp[i|(1<<k)][k]+=dp[i][j];
}
}
}
cout<<ans/2<<endl;
}
/*首先考虑到一个环其实是一条链两头连接起来的,
*所以我们只要找到所有的链判一下两头是否联通即可。
*但是一个链的状态是首尾和经过的点,是n^2*2^n的,
*考虑到题目限制n<=19,这个状态数显然是爆炸的。
*现在我们考虑人为地把一个环从经过的点中id最小的点的两侧处切开,
*则得到了两个以id最小值为起点的链。
*对于任意的环我们都可以这样做,
*于是我们只要统计以id最小值为起点的成环链的数量,将结果除以2即可。
*这样由于起点已经固定,状态数就只有n*2^n, 然后dp一下即可。
*dp的过程详见代码。
*[](http://blog.csdn.net/fangzhenpeng/article/details/49078233)
*/

输出任意一条哈密顿路径

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
#include <bits/stdc++.h>
using namespace std;
vector<int> adj[100005];
bitset<100005> vis;
vector<int> ans;
void dfs(int v1)
{
vis[v1]=true;
ans.push_back(v1);
for (int i=0; i<adj[v1].size(); i++)
{
if (!vis[adj[v1][i]])
{
dfs(adj[v1][i]);
break;
}
}
}
int main() {
// freopen("input.txt","r",stdin);
int n,m;
scanf("%d%d", &n, &m);
for (int i=0; i<m; i++)
{
int a,b;
scanf("%d%d", &a, &b);
a--; b--;
adj[a].push_back(b);
adj[b].push_back(a);
}
dfs(0);
reverse(ans.begin(),ans.end());
for (int i=0; i<adj[0].size(); i++)
{
if (!vis[adj[0][i]])
{
dfs(adj[0][i]);
break;
}
}
printf("%d\n", ans.size());
for (int i=0; i<ans.size(); i++)
printf("%d%c", ans[i]+1, i==ans.size()-1 ? '\n' : ' ');
}

cf 408

Bank Hacking

题意
题意:n个结点,n-1条边,每个点价值为a[i],两点有边直接相连,算相邻,i,j半相邻:存在没被攻击的中间点k,(i,k),(j,k)是相邻的
攻击i后,和i相邻和半相邻的点a[k]++,n<=3e5,求攻击n个点需要的最小x?
除了第一次外,每次攻击的点必须满足:1:online,2:和某个offline相邻,3:a[i]<=x

思路
n点,n-1条边且连通,则为无根的树,任取一点为根
关键在于条件2:每次能被攻击的点都要和offline相连->任意一点u的值最多+2
通过第一次操作后.对任意点u
若当u的祖先被攻击时,u最多+2,此时它的任意子结点v,只有当u被攻击时才能被攻击,子节点对u贡献为0
若为u的某个子树被攻击,u的其余子树和u的祖先 都只有在u被攻击时才被攻击,所以任意点u的值最多+2,ans<=mx+2

综上:第一次攻击的点+0,其相邻点+1 间接相邻点都+2,
C1为mx个数 C2为mx-1个数
ans=mx 只有当C1=1&&正好有C2个mx-1与mx相连
ans=mx+1 只有当存在一个点满足 其距离<=1内 mx的个数为C1
其余情况ans=mx+2

代码

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
#include<iostream>
#include<algorithm>
#include<cstdio>
#include <cstring>
#include<vector>
#define inf 0x7f7f7f7f
#define maxn 300050
using namespace std;
vector<int>e[maxn];
int a[maxn];
int n;
void solve()
{
int c1=0,c2=0,mx=-inf,u,ans=inf;
for(int i=1;i<=n;i++) mx=max(a[i],mx);
for(int i=1;i<=n;i++)
{
if(a[i]==mx)
{
c1++;
u=i;
}
else if(a[i]==mx-1)
c2++;
}
if(c1==1)
{
int cnt=0;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(a[v]==mx-1)
cnt++;
}
if(cnt==c2) ans=mx;
}
if(ans==inf)
{
for(int i=1;i<=n&&ans==inf;i++)
{
int cnt=0;
if(a[i]==mx) cnt++;
for(int j=0;j<e[i].size();j++)
{
int v=e[i][j];
if(a[v]==mx)
cnt++;
}
if(cnt==c1) ans=mx+1;
}
}
if(ans==inf) ans=mx+2;
printf("%d\n",ans);
}
int main()
{
/*freopen("input.txt","r",stdin);*/
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
solve();
return 0;
}

Police Stations

题意
有n个城市以及n-1条道路连接两个城市 路径长度为1
有k个警察局分布在城市中
问最多可以删除多少条路径 使所有城市最多走长度d后可以达到警察局

思路
因为保证有解 所以d是没有用的
确保最优解 使用bfs
每个警察局的vis初始化为1 队列初始压入所有的警察局
以每个警察局为根节点遍历 并把走过的点和边标记
最后没有标记的边就是可以删除的边

代码

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
#include<bits/stdc++.h>
#define maxn 300050
using namespace std;
struct node
{
int to,po;
node(int to,int po):to(to),po(po){}
};
vector<node>e[maxn];
int edge[maxn],vis[maxn];
int main(){
// freopen("input.txt","r",stdin);
int n,k,d;
while(scanf("%d%d%d",&n,&k,&d)!=EOF)
{
queue<int>q;
for(int i=0;i<=n;i++) e[i].clear();
memset(edge,0,sizeof(edge));
memset(vis,0,sizeof(vis));
for(int i=0;i<k;i++)
{
int x;
scanf("%d",&x);
vis[x]=1;
q.push(x);
}
for(int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(node(v,i));
e[v].push_back(node(u,i));
}
// cout<<e[1][0].to<<" "<<e[1][0].po<<endl;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].to;
if(vis[v]==0)
{
vis[v]=1;
edge[e[u][i].po]=1;
q.push(v);
}
}
}
int cnt=0;
for(int i=1;i<=n-1;i++)
if(edge[i]==0)
cnt++;
printf("%d\n",cnt);
for(int i=1;i<=n-1;i++)
if(edge[i]==0)
printf("%d ",i);
printf("\n");
}
return 0;
}

次小生成树

POJ 1679

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cstring>
#define maxn 105
using namespace std;
int vis[maxn];
int lowc[maxn];
int pre[maxn];
int Max[maxn][maxn];
int used[maxn][maxn];
int map[maxn][maxn];
const int INF=0x3f3f3f3f;
int prim(int n)
{
int ans=0;
memset(vis,0,sizeof(vis));
memset(Max,0,sizeof(Max));
memset(used,0,sizeof(used));
vis[0]=1;
pre[0]=-1;
for(int i=1;i<n;i++)
{
lowc[i]=map[0][i];
pre[i]=0;
}
lowc[0]=0;
for(int i=1;i<n;i++)
{
int minc=INT_MAX;
int p=-1;
for(int j=0;j<n;j++)
if(!vis[j]&&minc>lowc[j])
{
minc=lowc[j];
p=j;
}
if(minc==INT_MAX) return -1;
ans+=minc;
vis[p]=1;
used[p][pre[p]]=used[pre[p]][p]=1;
for(int j=0;j<n;j++)
{
if(vis[j]) Max[j][p]=Max[p][j]=max(Max[j][pre[p]],lowc[p]);
if(!vis[j]&&lowc[j]>map[p][j])
{
lowc[j]=map[p][j];
pre[j]=p;
}
}
}
return ans;
}
int ans;
int smst(int n)
{
int Min=INT_MAX;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(map[i][j]!=INT_MAX && !used[i][j])
Min=min(Min,ans+map[i][j]-Max[i][j]);
if(Min==INT_MAX) return -1;
return Min;
}
int main()
{
/*freopen("input.txt","r",stdin);*/
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(i==j)map[i][j]=0;
else map[i][j]=INT_MAX;
}
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
map[u-1][v-1]=w;
map[v-1][u-1]=w;
}
ans=prim(n);
if(ans==-1)
{
printf("Not Unique!\n");
continue;
}
if(ans==smst(n))
printf("Not Unique!\n");
else
printf("%d\n",ans);
}
return 0;
}

AT-coder(求矩形里子矩形的个数)

代码(公式)

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
#include<iostream>
#include<algorithm>
#define Maxn 100005
const int N=1e9+7;
#define maxn 100050
#define LL long long
using namespace std;
bool Cmp(int a,int b)
{
return a>b;
}
int main()
{
LL x[maxn],y[maxn],sumx=0,sumy=0;
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>x[i];
for(int i=0;i<m;i++)
cin>>y[i];
sort(x,x+n,Cmp);
sort(y,y+m,Cmp);
for(int i=0;i<=n-1;i++){
sumx=(sumx+(x[i]-x[i+1])*(n-1-i)*(i+1))%N;
}
for(int i=0;i<=m-1;i++){
sumy=(sumy+(y[i]-y[i+1])*(m-1-i)*(i+1))%N;
}
cout<<sumx*sumy%N<<endl;
return 0;
}

HDU 4305(生成树计数)

hihocoder-智力竞赛(dp)

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
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
const int inf=0x7f7f7f7f;
int a[1050];
int dp[1050][1050]; //dp[i][j]表示通过第i关 用了j次答题机会的最小答对题目个数
int main()
{
/*freopen("input.txt","r",stdin);*/
int T;
scanf("%d",&T);
while(T--)
{
int n,m,s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,inf,sizeof(dp));
for(int j=0;j<=m;j++)
dp[0][j]=0;
for(int i=1;i<=n;i++)
{
int num=ceil(a[i]/(float)s);
for(int k=0;k<=num;k++)
{
int x=a[i]-k*s;
if(x>0) x=ceil(x/(float)t);
else x=0;
for(int j=0;j<=m;j++)
if(x+k+j<=m)
dp[i][x+k+j]=min(dp[i][x+k+j],dp[i-1][j]+k);
}
}
int ans=inf;
for(int j=0;j<=m;j++)
ans=min(ans,dp[n][j]);
if(ans>m) printf("No\n");
else printf("%d\n",ans);
}
return 0;
}

UVA 10881(蚂蚁直线 蓝书P9)

atcoder_contest013C环状蚂蚁

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
#include <bits/stdc++.h>
using namespace std;
int n, l, t;
#define LL long long
const int N = 200000;
int xi[N], dir[N], xx[N];
int main()
{
// freopen("input.txt","r",stdin);
cin >> n >> l >> t;
LL c = 0;
for (int i = 0; i < n; ++ i)
{
cin >> xi[i] >> dir[i];
if (dir[i] == 2) dir[i] = -1;
int nx = xi[i] + t * dir[i];
c += (nx > 0? nx / l: (nx - l + 1) / l);
xx[i] = (nx % l + l) % l;
}
sort(xx, xx + n);
for (int i = 0; i < n; ++ i)
cout << xx[((i + c) % n + n) % n] << "\n";
}
//最后的求的数组xx其实也是一个环 c判断从何位置环形输出
//c:每次逆时针过原点-1 每次顺时针过原点+1
文章目录
  1. 1. 专题杂选
    1. 1.1. 线段树专题
    2. 1.2. 匈牙利算法+km算法
      1. 1.2.1. HDU 1150(匈牙利算法)
      2. 1.2.2. HDU 2255(km模版)
      3. 1.2.3. HDU 1533(km)
    3. 1.3. 哈密顿回路
      1. 1.3.1. 对比
      2. 1.3.2. cf 10D(求哈密顿回路的个数)
      3. 1.3.3. 输出任意一条哈密顿路径
    4. 1.4. cf 408
      1. 1.4.1. Bank Hacking
      2. 1.4.2. Police Stations
    5. 1.5. 次小生成树
      1. 1.5.1. POJ 1679
    6. 1.6. AT-coder(求矩形里子矩形的个数)
      1. 1.6.1. 代码(公式)
    7. 1.7. HDU 4305(生成树计数)
    8. 1.8. hihocoder-智力竞赛(dp)
    9. 1.9. UVA 10881(蚂蚁直线 蓝书P9)
    10. 1.10. atcoder_contest013C环状蚂蚁
|