百度之星初赛b
推一下发现是求c(n,m),范围很小直接预处理递推
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=1e9+7; 4 const int maxn=1008; 5 int c[maxn][maxn]; 6 void init() 7 { 8 memset(c,0,sizeof(c)); 9 c[0][0]=1; 10 for(int i=1;i<=1000;i++) 11 for(int j=0;j<=i;j++) 12 c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; 13 } 14 int main() 15 { 16 int t; 17 scanf("%d",&t); 18 init(); 19 int n,m; 20 while(t--) 21 { 22 scanf("%d%d",&n,&m); 23 if(n>m) 24 { 25 int temp=n; 26 n=m; 27 m=temp; 28 } 29 printf("%d\n",c[m][n]); 30 } 31 return 0; 32 }
可以转化为求lca问题。(为什么我做过的lca题时限都这么鬼畜呢,我以后永远选择树上倍增)
lca:求两个节点的最近公共祖先,一般有如下三种方法:
树上倍增算法:先将两个点移到同一高度,再让他们一起往上爬,直到相遇。对爬的过程进行了优化:建立一系列向上爬2^0高度,2^1,...2^n高度的边,使得点可以沿这些边加速爬来接近目标。可以用求树的重心法优化
tarjan算法:将所有询问离线处理,通过花式标记+简单并查集处理(个人认为这个方法是真的鬼畜,因为标记不少,又要维护集合,使得代码实际跑的速度比理论复杂度要大,常常出现O(n)跑不过O(nlogn)的情况,表示束手无策)
树刨+rmq:按照dfs序处理一条2n长的链,使得链中公共祖先一定在任意两个子节点的中间。然后rmq维护之,查询其中深度最小的点(表示非常蛋疼)
总之求lca是不难的,难点就在卡时间。就算用到了上述算法,但实现不够优秀,一样会t一脸。也不能说是被故意卡掉的,实在是rmq的测试数据都太大了,点多边多询问多,不小心可能连标程都卡掉这样子。。。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int N = 100010, INF = 0x3f3f3f3f; 6 struct edge 7 { 8 int to, cost, next; 9 }g[N*2]; 10 int cnt, head[N]; 11 int dep[N]; 12 int dis[N], p[N][20+5]; 13 bool vis[N]; 14 15 int n, m; 16 vector<int> vec[N]; 17 void init() 18 { 19 cnt=0; 20 memset(head,-1,sizeof(head)); 21 } 22 void add_edge(int v, int u, int cost) 23 { 24 g[cnt].to = u, g[cnt].cost = cost, g[cnt].next = head[v], head[v] = cnt++; 25 } 26 void dfs(int v, int fa, int cost) 27 { 28 dis[v] = cost; 29 for(int i = head[v]; i != -1; i = g[i].next) 30 { 31 int u = g[i].to; 32 if(u == fa) continue; 33 if(! dep[u]) 34 dep[u] = dep[v] + 1, p[u][0] = v, dfs(u, v, dis[v] + g[i].cost); 35 } 36 } 37 void init1() 38 { 39 for(int j = 1; (1<<j) <= n; j++) 40 for(int i = 1; i <= n; i++) 41 p[i][j] = p[p[i][j-1]][j-1]; 42 } 43 int LCA(int v, int u) 44 { 45 if(dep[v] < dep[u]) swap(v, u); 46 int d = dep[v] - dep[u]; 47 for(int i = 0; (d>>i) != 0; i++) 48 if((d>>i) & 1) v = p[v][i]; 49 if(v == u) return v; 50 for(int i = 20; i >= 0; i--) 51 if(p[v][i] != p[u][i]) v = p[v][i], u = p[u][i]; 52 return p[v][0]; 53 } 54 int main() 55 { 56 int t, q; 57 scanf("%d", &t); 58 while(t--) 59 { 60 init(); 61 scanf("%d%d", &n, &m); 62 for(int i = 1; i < n; i++) 63 { 64 int a, b, c; 65 scanf("%d%d%d", &a, &b, &c); 66 add_edge(a, b, c), add_edge(b, a, c); 67 } 68 for(int i = 1; i <= m; i++) 69 { 70 int num, v; 71 scanf("%d", &num); 72 for(int j = 1; j <= num; j++) 73 { 74 scanf("%d", &v); 75 vec[i].push_back(v); 76 } 77 } 78 memset(dis, 0, sizeof dis); 79 memset(dep, 0, sizeof dep); 80 dfs(1, 0, 0); 81 init1(); 82 scanf("%d", &q); 83 for(int i = 1; i <= q; i++) 84 { 85 int v, u, ans = INF; 86 scanf("%d%d", &v, &u); 87 for(size_t j = 0; j < vec[v].size(); j++) 88 for(size_t k = 0; k < vec[u].size(); k++) 89 ans = min(ans, dis[vec[v][j]] + dis[vec[u][k]] - dis[LCA(vec[v][j], vec[u][k])] * 2); 90 printf("%d\n", ans); 91 } 92 for(int i = 1; i <= m; i++) vec[i].clear(); 93 } 94 return 0; 95 }
费用流模板题,理解费用流就很容易a了。
#include<bits/stdc++.h> using namespace std; const int maxn=508; const int maxm=1008; const int INF=0x3f3f3f3f; struct fuck{ int u,v,cap,cost,next; }edge[maxm<<4]; int head[maxn]; int dis[maxn],pre[maxn],vis[maxn]; int tol; int n; void init() { tol=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int w,int c) { edge[tol].u=u; edge[tol].v=v; edge[tol].cap=c; edge[tol].cost=w; edge[tol].next=head[u]; head[u]=tol++; edge[tol].u=v; edge[tol].v=u; edge[tol].cap=0; edge[tol].cost=-w; edge[tol].next=head[v]; head[v]=tol++; } bool spfa() { queue<int> q; q.push(0); memset(dis,INF,sizeof(dis)); memset(vis,false,sizeof(vis)); dis[0]=0;vis[0]=true;pre[0]=-1; int i,u,v; while(!q.empty()) { u=q.front();q.pop(); vis[u]=false; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(edge[i].cap>0&&dis[v]>dis[u]+edge[i].cost) { dis[v]=dis[u]+edge[i].cost; pre[v]=i; if(!vis[v]) { vis[v]=true; q.push(v); } } } } if(dis[n]>=INF) return false; return true; } int min_costflow() { int co,fl,u,i; co=fl=0; while(spfa()) { int mi=INF; for (i = pre[n]; i != -1; i = pre[edge[i ^ 1].v]) { if (mi > edge[i].cap) mi = edge[i].cap; } for (i = pre[n]; i != -1; i = pre[edge[i ^ 1].v]) { edge[i].cap -= mi; edge[i ^ 1].cap += mi; } fl+=mi; if(dis[n]>0) break; co +=mi*dis[n]; } return co; } int main() { int m,u,v,a,b,c,d,w; while(scanf("%d%d",&n,&m)==2) { init(); for(int i=1;i<=n;i++) { scanf("%d%d%d%d",&a,&b,&c,&d); addedge(0,i,a,b); addedge(i,n+1,-c,d); } for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); addedge(u,v,w,INF); addedge(v,u,w,INF); } n++; printf("%d\n",-min_costflow()); } return 0; }
先合并一样区间,然后二分就可以了
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=100008; 4 struct fuck{ 5 int l,r; 6 }f[maxn],g[maxn]; 7 int cmp(fuck a,fuck b) 8 { 9 if(a.l!=b.l) 10 return a.l<b.l; 11 return a.r<b.r; 12 } 13 int idx; 14 void init(int n) 15 { 16 int i=1,j=i+1; 17 idx=0; 18 while(i<=n) 19 { 20 g[++idx]=f[i]; 21 while(j<=n) 22 { 23 if(f[j].l<=g[idx].r) 24 { 25 g[idx].r=max(f[j].r,g[idx].r); 26 j++; 27 } 28 else 29 break; 30 } 31 i=j; 32 } 33 } 34 int dis[maxn]; 35 int bs(int key,int n) 36 { 37 int left=1,right=n; 38 while(left<=right) 39 { 40 int mid=(left+right)>>1; 41 //printf("%d\n",mid); 42 if(dis[mid]>key) 43 right=mid-1; 44 else 45 left=mid+1; 46 } 47 return left-1; 48 } 49 int main() 50 { 51 int n,m; 52 while(scanf("%d%d",&n,&m)==2) 53 { 54 for(int i=1;i<=n;i++) 55 scanf("%d%d",&f[i].l,&f[i].r); 56 sort(f+1,f+n+1,cmp); 57 init(n); 58 dis[1]=0; 59 for(int i=2;i<=idx;i++) 60 dis[i]=g[i].l-g[i-1].r-1; 61 for(int i=2;i<=idx;i++) 62 dis[i]=dis[i-1]+dis[i]; 63 // for(int i=1;i<=idx;i++) printf("%d ",dis[i]);printf("\n"); 64 int ans=0; 65 for(int i=1;i<=idx;i++) 66 { 67 int x=bs(dis[i]+m,idx); 68 int len=g[x].r-g[i].l+1+m-dis[x]+dis[i]; 69 if(len>ans) 70 ans=len; 71 } 72 printf("%d\n",ans); 73 } 74 return 0; 75 }