专题杂选
线段树专题
参考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
| 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
| 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
| using namespace std; 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
| 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
| 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
| 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
| 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
| 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
| const int N=1e9+7; 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
| 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
| using namespace std; int n, l, t; 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
|