hdu 多校赛 第三场
slove 2/11
rank 288
补题 5/11
---------------------------------------------------
6604 Blow up the city
http://acm.hdu.edu.cn/showproblem.php?pid=6604
题意:给定一个DAG
定义一个点的指挥中心为这个点沿着边走到底的终点
给出q个查询,查询两个点,一次只能删除一个点,
如果删除一个点后输入的两个点之中有任何一个点无法到达原来的指挥中心
则方案数+1,求所有的方案数
思路:
由于是一个DAG,很容易想到建topo序.正向边建一个图,反向边也要建一个图
求topo序时在反向图中加一个超级源点,连接所有入度为0的点(网络流的思路..是不是很像)
定义一个节点的父亲为所有能到达这个节点的祖先的公共祖先.
由topo序,遍历到i点时1~i-1的所有节点已经处理完毕.
定义dep[i]=dep[fa]+1,注意dep不是简单地深度!
用支配树的角度讲就是这个点的支配树到根的链上的节点个数,也就是链长,是不是和深度很像..
然后答案就是dep[a]+dep[b]-dep[LCA(a,b)]
#include<bits/stdc++.h>
#define rep1(i,a,b) for(int i=a;i<=b;++i)
#define rep2(i,b,a) for(int i=b;i>=a;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pf push_front
#define pob pop_back
#define pof pop_front
#define ll long long
using namespace std;
const int MAX = 2e5+5;
const int INF = 0x3f3f3f3f;
int n,m,first1[MAX],nextt1[MAX],u1[MAX],v1[MAX],cnt1=0,du[MAX];
int first2[MAX],nextt2[MAX],u2[MAX],v2[MAX],top[MAX],cnt2=0,rt;
int tot=0,f[MAX][30],dep[MAX];
void add1(int a,int b){u1[cnt1]=a,v1[cnt1]=b;nextt1[cnt1]=first1[u1[cnt1]];first1[u1[cnt1]]=cnt1;++cnt1;
}
void add2(int a,int b){u2[cnt2]=a,v2[cnt2]=b;nextt2[cnt2]=first2[u2[cnt2]];first2[u2[cnt2]]=cnt2;++cnt2;
}
void topology(){deque<int> list1;rep1(i,1,n) if(!du[i]) list1.pb(i);while(!list1.empty()){int now = list1.front();list1.pof();top[++tot]=now;for(int num = first2[now];num!=-1;num=nextt2[num]){--du[v2[num]];if(!du[v2[num]]) list1.pb(v2[num]);}}
}
int LCA(int x,int y){if(dep[x]>dep[y]) swap(x,y);rep2(i,20,0) if(dep[y]>dep[x]&&dep[f[y][i]]>=dep[x]) y=f[y][i];rep2(i,20,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];return x==y?x:f[x][0];
}
void solve(){int t;scanf("%d",&t);while(t--){cnt1=cnt2=tot=0;mem(first1,-1);mem(first2,-1);mem(du,0);mem(f,0);mem(dep,0);scanf("%d%d",&n,&m); rt=n+1;rep1(i,1,m){int a,b;scanf("%d%d",&a,&b);add1(a,b),add2(b,a); ++du[a];}rep1(i,1,n) if(du[i]==0) add1(i,rt),add2(rt,i);topology();rep1(i,1,n){int now = top[i],fa=-1;for(int num = first1[now];num!=-1;num=nextt1[num]) fa=fa==-1?v1[num]:LCA(v1[num],fa);dep[now]=dep[fa]+1; f[now][0]=fa;rep1(j,1,20) f[now][j]=f[f[now][j-1]][j-1];}int q;scanf("%d",&q);while(q--){int a,b;scanf("%d%d",&a,&b);int ans = dep[a]+dep[b]-dep[LCA(a,b)];printf("%d\n",ans);}}
}
int main(void)
{solve();return 0;
}
6606 Distribution of books
http://acm.hdu.edu.cn/showproblem.php?pid=6606
题意:n个数,连续的一段分为一份,总共分为k份,要求每份和的最大值尽量小
1<=n<=2e5,-1e9<=ai<=1e9,1<=k<=n
note:最后不一定分完,但前缀一定要分完。
做法:
1、考虑二分答案,check的时候dp[i] 表示区间 最多能被分为多少份,
2、假设二分的答案是kk,然后对于 dp[i] 来说,他能被分成的份数,就是所有大于sum[i]-kk 能被分成的份数+1,可以这样理解,如果他的一个前缀大于 sum[i]-kk ,那么 i 和 j 之间的和一定是小于等于 kk 的,就是说可以构成一份。
3、由于可能存在构成0份的情况,比如所有值都比 kk 要大,所以预设最大值为极小值,当某个前缀和值小于等于 kk 时,那么这个点最少可以分成一份,其他点也会由这个点转移,因为其他店是极小的,不能构成一份。
4、直接dp复杂度 ,显然超时,所以用线段树来维护前缀和最大值,这样复杂度
#include <bits/stdc++.h>
#define lson left,mid,k<<1
#define rson mid+1,right,k<<1|1
#define imid int mid=(left+right)/2;
#define ll long long
using namespace std;
const int MAXN = 2e5 + 5;
struct node
{int l;int r;ll maxn;
}que[MAXN * 4];
int n, k, ql, qr, qq;
ll a[MAXN], t[MAXN], val;
void up(int k)
{que[k].maxn = max(que[k << 1].maxn, que[k << 1 | 1].maxn);
}
void build(int left = 1, int right = n, int k = 1)
{que[k].l = left;que[k].r = right;if (left == right){que[k].maxn = -1e18;return;}imid;build(lson);build(rson);up(k);
}
void update(int left = 1, int right = n, int k = 1)
{if (qr < left || right < ql)return;if (ql <= left && right <= qr){que[k].maxn = max(que[k].maxn, val);return;}imid;update(lson);update(rson);up(k);
}
ll query(int left = 1, int right = n, int k = 1)
{if (qr < left || right < ql)return -1e18;if (ql <= left && right <= qr)return que[k].maxn;imid;return max(query(lson), query(rson));
}
bool check(ll kk)
{build();for (int i = 1; i <= n; i++){/*dp[i]表示前i个数最多能分成几段for (int i = 1; i <= n; i++){dp[i] =max(dp[j]) + 1;(sum[i]-sum[j]<=mid)sum[j]>=sum[i]-mid}*/ll temp = t[a[i]] - kk;temp = lower_bound(t + 1, t + 1 + qq, temp) - t;//离散化ql = temp; qr = n;val = query() + 1;//找到所有大于sum[i]-kk的最大组数if (t[a[i]] <= kk)//单个物品合法,所以这个点的最小组数是1val = max(val, 1LL);if (val >= k)return true;ql = a[i], qr = a[i];//更新前缀和为a[i]的最大组数valupdate();}return false;
}
int main()
{int T;scanf("%d", &T);while (T--){scanf("%d%d", &n, &k);for (int i = 1; i <= n; i++){scanf("%lld", &a[i]);a[i] += a[i - 1];t[i] = a[i];}sort(t + 1, t + 1 + n);qq = unique(t + 1, t + 1 + n) - t - 1;for (int i = 1; i <= n; i++)a[i] = lower_bound(t + 1, t + 1 + qq, a[i]) - t;ll l = -2e14 - 5, r = 2e14 + 5;while (l + 1 < r){ll mid = (l + r) / 2;if (check(mid))r = mid;elsel = mid;}if (!check(l))l = r;printf("%lld\n", l);}
}
6608 Fansblog
http://acm.hdu.edu.cn/showproblem.php?pid=6608
题意:给一个质数N,找出小于N的最大的质数P,并且输出P的阶乘模N。
思路:米勒罗宾或者暴力找出小于N的最大的质数P。
由威尔逊定理 ,我们要求
Code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll ModMul(ll a, ll b, ll n)
{ll ans = 0;while (b){if (b & 1)ans = (ans + a) % n;a = (a + a) % n;b >>= 1;}return ans;
}
ll ModExp(ll a, ll b, ll n)
{ll ans = 1;while (b){if (b & 1)ans = ModMul(ans, a, n);a = ModMul(a, a, n);b >>= 1;}return ans;
}
bool miller_rabin(ll n)
{ll i, j, a, x, y, t, u, s = 10;if (n == 2)return true;if (n < 2 || !(n & 1))return false;for (t = 0, u = n - 1; !(u & 1); t++, u >>= 1);for (i = 0; i < s; i++){a = rand() % (n - 1) + 1;x = ModExp(a, u, n);for (j = 0; j < t; j++){y = ModMul(x, x, n);if (y == 1 && x != 1 && x != n - 1)return false;x = y;}if (x != 1)return false;}return true;
}
ll MOD = 1000000007;
ll inv(ll a)
{return ModExp(a, MOD - 2, MOD);
}
int main()
{int T;scanf("%d", &T);while (T--){ll n;scanf("%lld", &n);MOD = n;ll p = n - 1;while (true){if (miller_rabin(p))break;elsep--;}ll ans = 1;if (p + 2 == n){printf("1\n");continue;}for (ll i = p + 1; i <= n - 2; i++)ans = ModMul(ans, inv(i), MOD);printf("%lld\n", ans);}
}
6609 Find the answer
http://acm.hdu.edu.cn/showproblem.php?pid=6609
题意:以1为左端点,i为右端点,要满足和小于等于M,至少需要将多少个数字变成0?
思路:二分取的数值的最大值,然后判一下能否取到下一个数值,取几个(就是说,下一个数值可能没有全部取,只需一部分),树状数组或者线段树维护前缀和和前缀个数。需要离散化
(Codeforces原题链接 :http://codeforces.com/contest/1185/problem/C2)数据弱化版本
Code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 2e5 + 5;
ll mp[MAXN];
int a[MAXN];
int t[MAXN];
ll c_cnt[MAXN];
ll c_sum[MAXN];
int lowbit(int k)
{return k& (-k);
}
void update(int k, ll x, ll val)
{while (k < MAXN){c_cnt[k] += x;c_sum[k] += val;k += lowbit(k);}
}
ll query(int k)
{ll ans = 0;while (k){ans += c_sum[k];k -= lowbit(k);}return ans;
}
ll querycnt(int k)
{ll ans = 0;while (k){ans += c_cnt[k];k -= lowbit(k);}return ans;
}
bool check(int k, ll m)
{ll ans = query(k);if (ans <= m)return true;return false;
}
int main()
{int T;scanf("%d", &T);while (T--){int n;ll m;scanf("%d%lld", &n, &m);for (int i = 1; i <= n; i++){c_cnt[i] = 0;c_sum[i] = 0;scanf("%lld", &a[i]);t[i] = a[i];}sort(t + 1, t + 1 + n);int qq = unique(t + 1, t + 1 + n) - t - 1;for (int i = 1; i <= n; i++){int temp = lower_bound(t + 1, t + 1 + qq, a[i]) - t;mp[temp] = a[i];a[i] = temp;}printf("0 ");for (int i = 2; i <= n; i++){update(a[i - 1], 1, mp[a[i - 1]]);int l = 0, r = qq;while (l + 1 < r){int k = (l + r) / 2;if (check(k, m - mp[a[i]]))l = k;elser = k;}if (check(r, m - mp[a[i]]))l = r;ll ans = querycnt(l);ll sum = query(l);if (l < qq){ll cnt = querycnt(l + 1) - querycnt(l);int num = min((m - mp[a[i]] - sum) / t[l + 1], cnt);ans += num;}printf("%lld ", i - ans - 1);}printf("\n");}
}
6611 K Subsequence
题意:给你一个序列,让你取出k个不降子序列,求最大权值和,一个元素只能取一次
思路: 经典网络24流里有一题是最长递增子序列.那个题意是先让你求出一个序列的最长递增子序列
长度是多少,求出的答案为s,然后让你求原序列有多少个长度为s的递增序列.
那个题目的思路是拆点法.先建一个源点和汇点,然后将每个序列中的点拆成ix,iy.
源点和每个ix连有边权为1的边,ix和iy之间有一条边权为1的边,然后当j>i&&a[j]>=a[i]时,连一条
iy->jx的边,然后根据之前的DP让每个dp[i]==s的点和汇点连一条边权为1的边,然后跑一边网络流
那么这个题目就和上个题目很相似了.题目是让我们求最大权值和,如果做过上题的话显然可以想到
费用流.那这就好办啦,题目要求是k个,先把源点拆成sl,sr,然后sl,sr的边权为k.
然后把序列的每个点拆成ix,iy,和上题的思路一样,ix和iy之间有一条边权为1的边,
然后当j>i&&a[j]>=a[i]时,连一条iy->jx的边,然后sr->ix,iy->t都有一条权为1的边,然后
这是费用流,肯定要有费用的!只需要把ix->iy边的费用设为该点的权值,其他边的费用都为0
然后跑一边费用流就好啦
有一个点需要注意:ix->iy和 当j>i&&a[j]>=a[i]时,连一条iy->jx的边,这两种边不能搞反了
考虑一种特殊情况,整个原序列为 6 5 4 3 2 1,如果把上述边反向建的话你就会发现跑不到汇点了,
答案为0,显然答案不为0.
#include<bits/stdc++.h>
#define rep1(i,a,b) for(int i=a;i<=b;++i)
#define rep2(i,b,a) for(int i=b;i>=a;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pf push_front
#define pob pop_back
#define pof pop_front
#define sc scanf
#define pr printf
#define ll long long
using namespace std;
typedef pair<int,int> P;
const int MAXN = 1e5+5;
const int INF = 0x3f3f3f3f;
struct side{ int to,cap,cost,rev;};
int n,m,flow,s,t,s1,h[MAXN],mincost;
vector<side> first[MAXN];
int dis[MAXN],sideOne[MAXN],sideTwo[MAXN],a[MAXN];
void minCostMAXFlow(int f)
{fill(h,h+t,0);while(f > 0){priority_queue<P,vector<P>, greater<P> > list1;rep1(i,0,t) dis[i]=INF;dis[s] = 0; list1.push(P(0,s));while(!list1.empty()) {P now = list1.top(); list1.pop();if(dis[now.second] < now.first) continue;int v; v = now.second;for(int i=0;i<(int)first[v].size();++i){side &e = first[v][i];if(e.cap > 0 && dis[e.to] > dis[v] + e.cost + h[v] - h[e.to]){dis[e.to] = dis[v] + e.cost + h[v] - h[e.to];sideOne[e.to] = v,sideTwo[e.to] = i;list1.push(P(dis[e.to],e.to));}}}if(dis[t] == INF) break;rep1(i,0,t) h[i] += dis[i]; int d = f;for(int v=t;v!=s;v=sideOne[v]) d = min(d,first[sideOne[v]][sideTwo[v]].cap);f=f-d,flow =flow+ d,mincost =mincost+ d * h[t];for(int v=t;v!=s;v=sideOne[v]){side &e = first[sideOne[v]][sideTwo[v]];e.cap -= d,first[v][e.rev].cap += d;}}
}
void add(int from,int to,int cap,int cost){first[from].pb({to,cap,cost,(int)first[to].size()});first[to].pb({from,0,-cost,(int)first[from].size()-1});
}
void solve(){int tt;sc("%d",&tt);while(tt--){flow=mincost=0;scanf("%d%d",&n,&m);rep1(i,1,2*n+3) first[i].clear();rep1(i,1,n) sc("%d",&a[i]);s=2*n+1,s1=2*n+2,t=2*n+3;add(s,s1,m,0);rep1(i,1,n) add(s1,i,1,0),add(i,i+n,1,-a[i]),add(i+n,t,1,0);rep1(i,1,n) rep1(j,i+1,n) if(a[i]<=a[j]) add(i+n,j,1,0);minCostMAXFlow(INF);pr("%d\n",-mincost);}
}int main(void)
{solve();return 0;
}
hdu 多校赛 第三场相关推荐
- 2015 多校赛 第三场 1002 (hdu 5317)
Description Mr. Hdu is interested in Greatest Common Divisor (GCD). He wants to find more and more i ...
- HDU 多校联合第三场
我发现已经无力吐嘈这几次的比赛了...这次蹭数据的少了许多,不过出题报告讲的那叫一个玄而又玄,完全没理解他要表达什么 http://page.renren.com/601081183/note/863 ...
- 2022杭电多校赛第八场
2022杭电多校赛第八场 文章目录 2022杭电多校赛第八场 1004.Quel'Thalas 1001.Theramore 1011.Stormwind 1008.Orgrimmar 1005.Ir ...
- 2021湖南多校对抗赛第三场
2021湖南多校对抗赛第三场 排名 第一 第二 第三 中南2队 中南1队 湘潭大学1队 团体成绩 学校 总题数 总罚时 中南大学 26 4396 题解(部分) special thanks: Binb ...
- 2022湖南多校对抗赛第三场
2022湖南多校对抗赛第三场 队伍排名 第一 第二 第三 中南大学1队 国防科技大学1队 湖南师范大学3队 团体成绩 取每个学校正式队伍前四名 学校 总题数 中南大学 27 湖南师范大学 27 湖南大 ...
- 2021年度训练联盟热身训练赛第三场赛后补题
2021年度训练联盟热身训练赛第三场赛后补题 A Circuit Math [题目分析] [代码展示] B Diagonal Cut [题目分析] [代码展示] C Gerrymandering [题 ...
- 2021年度训练联盟热身训练赛第三场(待补)
文章目录 前言 一.Circuit Math(后缀表达式---栈&&fgets) 二.Diagonal Cut(gcd最大公因数,数论) 三.expected primary-expr ...
- 2013多校训练赛第三场 总结
HDU 4621~4631 今天的多校好变态,是IOI冠军出的题,把我们虐的半死了. 简单讲一下今天的情况,今天就只做了两道水题,算是签了个到,然后就卡1011(HDU 4631)一个下午了.其实感觉 ...
- hdu 多校赛 第二场
slove 3/12 rank 224 补题 6/12 --------------------------------------------------- hdu 6595 http:// ...
最新文章
- 程序江湖:第十四章 离开让男人成长
- U盘、硬盘格式化数据恢复
- linux添加审计账户_眼镜蛇W眼镜蛇白盒品白源代码审计工具 白帽子版
- 函数调用oracle,oracle 函数调用
- 使用Github+Hexo框架搭建部署自己的博客
- PCB布局布线基础技巧问答_“Altium杯”Altium_Designer应用技巧
- RedisJson 是什么?比ES快 500 倍?
- 【elasticsearch】ES数据库重建索引 -- Reindex(数据迁移)
- vivo手机充电慢还耗电
- 点滴积累【JS】---JS小功能(JS实现多物体缓冲运动)
- 关于GraphEasy的简单捣腾
- 《锋利的jQuery》二、jQuery的选择器
- 《全球变化与地球系统科学》章节测试答案
- 计算机教室管理员应知应会,教室管理
- 计算机毕业设计node+vue基于微信小程序的乐团团购系统的设计与实现
- javaweb_XML学习
- BeautifulSoup爬取唯美girls小姐姐图片
- 破开C语言暗夜的第二道光 —— 数据与数据类型(1)
- H5响应式网站制作那些事
- 玩转开发板--Linux系统移植至开发板fl2440实践过程