【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP
来源:互联网 发布:手机在线编程网站 编辑:程序博客网 时间:2024/05/16 17:19
cyyz&qhyz&lwyz&gryz弱校胡策 命题人:cyyz ws_fqk
T3暴力写挫了 50+10+0滚粗辣!
奇妙的约会(appointment.cpp/c/pas)
【问题描述】
DQS和sxb在网上结识后成为了非常好的朋友,并且都有着惊人
的OI水平。在NOI2333的比赛中,两人均拿到了金牌,并保送进入
HU/PKU。于是两人决定在这喜大普奔的时刻进行面基。
NOI2333参赛选手众多,所以安排了n个考点,DQS在1号考点,
而sxb在n号考点。由于是举办全国性赛事的城市,自然有许多奇妙
的性质:
某些考点会组成一个集合, 集合中的考点两两之间的距离为一固
定值,且一个考点可以属于多个集合。
形式的说:有n个考点,m个集合,集合i中包含s_i个考点,
且这s_i个考点之间两两距离为t_i。
金牌爷的时间都非常珍贵,现在他们想知道,两人同时出发,他
们到达同一个考点见面所需的最短时间是多少?并且他们想知道, 满
足时间最短的考点分别是哪些?
【输入】
第一行包含两个整数n,m,意义如题意所说。
接下来m行,首先每行会有两个整数,分别代表t_i,s_i,接下
来包含s_i个整数,表示集合i中所含的考点编号。
【输出】
若两人无法见面,则输出“impossible”。
否则,第一行输出一个整数,表示最短时间。
第二行输出满足时间最短的考点编号,相邻的用空格隔开。
【样例输入】
5 41 3 1 2 32 2 3 410 2 1 53 3 3 4 5
【样例输出】
33 4
【数据范围】
对于50%的数据
N<=1000,sigma(s_i)<=1000
对于100%的数据
2<=n<=10^5,1<=t_i<=10^9,s_i>0,sigma(s_i)<=10^6
n^2建边 50分
%学娣 @Loi_a 的玄学暴力在开O2O3的前提下A掉此题…
可以发现每个集合并不需要建这么多边。新建两个点表示次集合的入点、出点,然后集合中每个点向入点建权值为0的边,出点向每个点建权值为0的边,入点向出点建权值为t的边,跑两遍最短路即可。
据说原题是doc出的题?
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;typedef long long LL; const int SZ = 3000010;const int INF = 1000000010;int head[SZ],nxt[SZ];struct edge{ int t; LL d;}l[SZ];void build(int f,int t,LL d){ static int tot = 1; l[++ tot].t = t; l[tot].d = d; nxt[tot] = head[f]; head[f] = tot;}LL dist1[SZ],dist2[SZ];struct Heap{ int u; LL d; Heap(int a = 0,LL b = 0) : u(a),d(b) {}};bool operator <(Heap a,Heap b){ return a.d > b.d;}priority_queue<Heap> q;bool vis[SZ];void spfa(int s){ memset(dist2,63,sizeof(dist2)); memset(vis,0,sizeof(vis)); dist2[s] = 0; q.push(Heap(s,0)); while(q.size()) { int u = q.top().u; q.pop(); if(vis[u]) continue; vis[u] = 1; for(int i = head[u];i;i = nxt[i]) { int v = l[i].t; if(dist2[v] > dist2[u] + l[i].d) { dist2[v] = dist2[u] + l[i].d; q.push(Heap(v,dist2[v])); } } }}int main(){ freopen("appointment.in","r",stdin); freopen("appointment.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= m;i ++) { int t,s; scanf("%d%d",&t,&s); int in = i + 100000,out = i + 100000 * 2; build(in,out,t); while(s --) { int x; scanf("%d",&x); build(x,in,0); build(out,x,0); } } spfa(1); for(int i = 1;i <= n;i ++) dist1[i] = dist2[i]; spfa(n); LL maxd = INF; for(int i = 1;i <= n;i ++) maxd = min(maxd,max(dist1[i],dist2[i])); if(maxd >= INF) { puts("impossible"); return 0; } printf("%lld\n",maxd); for(int i = 1;i <= n;i ++) if(max(dist1[i],dist2[i]) == maxd) printf("%d ",i); fclose(stdin); fclose(stdout); return 0;}
粗心的特派员(careless.cpp/c/pas)
【问题描述】
在APIO&CTSC2016名单下发时,yzy和fqk惊奇的发现名单中没
有他们的名字,于是急忙联系了山东省特派员豆包哥lpy。却被很无
奈的告知报名表被当成垃圾邮件扔进了垃圾桶。
粗心的特派员总是会做一些让人啼笑皆非的事情, 比如在给各位
选手回复邮件的时候,经常会因为眼花而发错人。但虽然眼花,所以
误发的范围也不会太大。具体地说,本应该发给第i个人的邮件,可
能会不小心发给编号为[i-2,i+2]的人, 比如按照NOIP名单的顺序应
该是lct1999,Oxer,TA,fye,davidxu,而本应该发给TA的邮件却可
能发给这五位神犇中的任意一位。 但是他会保证每人都会且仅会收到
一封邮件。现在他想知道,他要将n封邮件发给n位选手,会有多少
种不同的结果?由于答案可能很大,请对10^9+7取模后输出。
【输入】
一行,一个整数n。
【输出】
一行,一个整数表示答案。
【样例输入】
4
【样例输出】
14
【数据范围】
对于10%的数据,n<=10
对于40%的数据,n<=500000
对于100%的数据,n<=10^16
第i个数可以放到[i-2,i+2]的区间内,求长度为n的数列的合法排列方案数。
出题人给的做法是状压DP,dp[i][S]表示当前填到第i个数,[i-2,i+2]填/不填的状态是S的方案数。要时刻保持[1,i-3]全为1,这样就能转移了。
发现每次转移都是一样的,可以矩阵优化。32*32的矩阵。
玄学做法:暴力跑出前十项,然后强行设系数然后高斯消元…最后发现是f[n]=2f[n-1]+2[n-3]-f[n-5]……事实证明这是对的,然后矩阵就行了。
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;typedef long long LL; const int SZ = 500010;const int INF = 1000000010;const int mod = 1000000007;struct matrix{ int n,m; int num[20][20]; matrix(int a = 0,int b = 0) :n(a),m(b) { memset(num,0,sizeof(num)); }};matrix operator *(const matrix &a,const matrix &b){ matrix ans(a.n,b.m); for(int i = 1;i <= ans.n;i ++) for(int j = 1;j <= ans.m;j ++) for(int k = 1;k <= a.m;k ++) ans.num[i][j] = (ans.num[i][j] + (LL)a.num[i][k] * b.num[k][j] % mod) % mod; return ans;}matrix ans,f;matrix ksm(matrix a,LL b){ while(b) { if(b & 1) ans = ans * a; a = a * a; b >>= 1; } return ans;}void init(){ ans.n = 1; ans.m = 5; f.n = 5; f.m = 5; ans.num[1][1] = 31; ans.num[1][2] = 14; ans.num[1][3] = 6; ans.num[1][4] = 2; ans.num[1][5] = 1; f.num[1][1] = 2; f.num[3][1] = 2; f.num[5][1] = -1; f.num[1][2] = 1; f.num[2][3] = 1; f.num[3][4] = 1; f.num[4][5] = 1;}int main(){ freopen("careless.in","r",stdin); freopen("careless.out","w",stdout); LL n; scanf("%lld",&n); init(); if(n <= 5) printf("%d\n",ans.num[1][5 - n + 1]); else { ksm(f,n - 5); printf("%d\n",(ans.num[1][1] + mod) % mod); } return 0;}/*1 12 23 64 145 31f[n] = 2f[i - 1] + 2 * f[i - 3] - f[i - 5]*/
Description
浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略。这个城市可以看成一棵有n个节点的有根树,我们把每个节点用1到n的整数编号。为了方便起见,对于任何一个非根节点v,它任何一个祖先的编号都严格小于v。树上的每个节点表示一个矿点,每条边表示一条街道。作为cg大军的一个小队长,你拥有m个部下。你有一张二维的动态信息表,用Ti,j表示第i行第j列的数据。当你被允许开采某个区域时,你可以将你的部下分配至各个矿点。在第i个矿点安排j个人可以获得Ti,j单位的矿产。允许开采的区域是这样描述的:给你一对矿点(u,v),保证v是u的祖先(这里定义祖先包括u本身);u为你控制的区域,可以在以u为根的子树上任意分配部下;u到v的简单路径(不包括u但包括v,若u=v则包括u)为探险路径,在该路径上你可以选择至多一个矿点安排部下。你这次开采的收益为安排有部下的矿点的收益之和。
Input
输入的第一行包含5个正整数n、m、A、B、Q。n为矿点的个数,m为部下的数量。A、B、Q是与动态信息表有关的数据。第二行包含n-1个正整数,第i个数为Fi+1,表示节点i+1的父亲。接下来需要你用下文的方法依次生成n组数据,每组数据共m个。其中第i组的m个数据为信息表中第i行的m个数据。紧接着一行包含一个正整数C,表示事件的数量。最后给出C行,每行描述一个事件。每个事件会先给出一个0或1的整数。如果该数为0,则后面有一个正整数p,表示动态信息表有更新,你需要生成一组m个数据,来替换信息表中第p行的m个数据。如果该数为1,则后面有两个正整数u、v,表示出现了一个你可以开采的区域,你需要回答这次开采的收益。同一行的各个数之间均用一个空格隔开,没有多余的空格和换行。数据的生成方法如下:每次生成一组m个从小到大排列的数据,替换动态信息表的一行。其中,从小到大第j个数替换信息表中第j列的数。调用以下代码m次并排序得到一组数据。(注意可能会出现重复的数)函数GetInt A←((A xor B)+(B div X)+(B * X))and Y B←((A xor B)+(A div X)+(A * X))and Y 返回(A xor B)mod Q 其中A、B、Q均用32位有符号整数保存(C/C++的signed long int类型,pascal的longint类型),X=216(2的16次方),Y=231-1(2的31次方-1),xor为位异或运算,div为整除运算,and为位且运算,mod为取余运算。由于只保留了低31位,易得我们不用考虑数据的溢出问题。(注意每次A和B都会被改变)
Output
对于每个开采事件(开头为1的事件),输出一行一个整数,为每次的收益。
Sample Input
10 5 1 2 101 1 3 3 4 4 6 6 941 6 31 9 10 11 1 1
Sample Output
11912
【样例说明】
最初的信息表如下
1 2 3 4 51 0 1 1 2 22 0 5 7 7 93 1 2 3 4 54 0 1 2 4 55 2 4 7 8 86 0 2 3 8 97 1 3 5 6 88 3 3 3 7 89 0 1 2 3 910 0 0 1 4 4
变化后的第1行为
1 1 1 1 4 7
第一次开采可以在矿点6、8、9、10任意安排,可以在矿点3或4中选取一个安排开采。一种最优安排是在矿点6安排4人,在矿点8安排1人。第二次开采可以在矿点9安排,可以在矿点6、4、3、1中选择一个安排。一种最优安排是在矿点9安排1人,在矿点6安排4人。
HINT
有50%的数据,对于满足2≤i≤n的整数i,Fi=i-1。这些数据中有40%的数据(即所有数据的20%)满足n≤500,m≤20,C≤500。除上述数据,另有40%的数据满足n≤500,m≤20,C≤500。对于100%的数据1≤n≤20000,1≤m≤50,1≤C≤2000。对于满足2≤i≤n的整数i,1≤Fi<i。1≤A,B≤231-1,1≤Q≤10000。
Source
题面太长,复制的bzoj的。
清橙:http://www.tsinsen.com/A1219。
线段树每个点维护当前区间选一个点放i个人的最大价值,再来个随便放i个点的最大价值,然后合并的时候像背包那样合并就行了
毒瘤题。
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;typedef long long LL; const int SZ = 100010;const int INF = 1000000010;int n,m,A,B,Q;int get_int(){ const int X = 1 << 16; const int Y = (1ll << 31ll) - 1ll; A=((A^B)+(B/X)+(B*X))&Y; B=((A^B)+(A/X)+(A*X))&Y; return (A^B)%Q;}void scan(int &n){ n = 0; char a = getchar(); bool flag = 0; while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); } while(a <= '9' && a >= '0') { n = n * 10 + a - '0'; a = getchar(); } if(flag) n = -n;}int head[SZ],nxt[SZ],to[SZ];void build(int f,int t){ static int tot = 1; to[++ tot] = t; nxt[tot] = head[f]; head[f] = tot;}int top[SZ],sz[SZ],son[SZ],fa[SZ],deep[SZ];void dfs_1(int u,int f){ fa[u] = f; deep[u] = deep[f] + 1; sz[u] = 1; for(int i = head[u];i;i = nxt[i]) { int v = to[i]; dfs_1(v,u); sz[u] += sz[v]; if(!son[u] || sz[son[u]] < sz[v]) son[u] = v; }}int pre[SZ],suf[SZ],dfs_clock = 0,intre[SZ];void dfs_2(int u,int topu){ top[u] = topu; pre[u] = ++ dfs_clock; intre[dfs_clock] = u; if(son[u]) dfs_2(son[u],topu); for(int i = head[u];i;i = nxt[i]) { int v = to[i]; if(v == son[u]) continue; dfs_2(v,v); } suf[u] = ++ dfs_clock;}int val[SZ][60];struct segment{ int l,r; int mx1[60],mx2[60]; //子树 链 }tree[SZ << 2];void update(int p){ memset(tree[p].mx1,0,sizeof(tree[p].mx1)); int lch = p << 1,rch = p << 1 | 1;// for(int i = 0;i <= m;i ++)// for(int j = 0;j <= m - i;j ++)// tree[p].mx1[i + j] = max(tree[p].mx1[i + j],tree[lch].mx1[i] + tree[rch].mx1[j]); for(int i = m;i >= 0;i --) for(int j = i;j >= 0;j --) tree[p].mx1[i] = max(tree[p].mx1[i],tree[lch].mx1[j] + tree[rch].mx1[i - j]); for(int i = 1;i <= m;i ++) tree[p].mx2[i] = max(tree[lch].mx2[i],tree[rch].mx2[i]);}void build(int p,int l,int r){ tree[p].l = l; tree[p].r = r; if(l == r) { for(int i = 1;i <= m;i ++) tree[p].mx1[i] = tree[p].mx2[i] = val[intre[l]][i]; return ; } int mid = (l + r) >> 1; build(p << 1,l,mid); build(p << 1 | 1,mid + 1,r); update(p);}void change(int p,int pos){ if(tree[p].l == tree[p].r) { for(int i = 1;i <= m;i ++) tree[p].mx1[i] = tree[p].mx2[i] = val[intre[pos]][i]; return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(pos <= mid) change(p << 1,pos); else change(p << 1 | 1,pos); update(p);}int ans1[60],ans2[60];void ask_ans1(int p,int l,int r){ if(l <= tree[p].l && tree[p].r <= r) { for(int i = m;i >= 0;i --) for(int j = i;j >= 0;j --) ans1[i] = max(ans1[i],ans1[i - j] + tree[p].mx1[j]); return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(l <= mid) ask_ans1(p << 1,l,r); if(mid < r) ask_ans1(p << 1 | 1,l,r);}void ask_ans2(int p,int l,int r){ if(l <= tree[p].l && tree[p].r <= r) { for(int i = 1;i <= m;i ++) ans2[i] = max(ans2[i],tree[p].mx2[i]); return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(l <= mid) ask_ans2(p << 1,l,r); if(mid < r) ask_ans2(p << 1 | 1,l,r);}void find_ans2(int x,int y){ int fx = top[x],fy = top[y]; while(fx != fy) { if(deep[fx] < deep[fy]) swap(fx,fy),swap(x,y); ask_ans2(1,pre[fx],pre[x]); x = fa[fx]; fx = top[x]; } if(deep[x] > deep[y]) swap(x,y); ask_ans2(1,pre[x],pre[y]);}int main(){ freopen("energy.in","r",stdin); freopen("energy.out","w",stdout); scan(n); scan(m); scan(A); scan(B); scan(Q); for(int i = 2;i <= n;i ++) { int x; scan(x); build(x,i); } for(int i = 1;i <= n;i ++) { for(int j = 1;j <= m;j ++) val[i][j] = get_int(); sort(val[i] + 1,val[i] + 1 + m); } dfs_1(1,0); dfs_2(1,1); build(1,1,dfs_clock); int C; scan(C); while(C --) { int opt; scan(opt); if(opt == 0) { int p; scan(p); for(int j = 1;j <= m;j ++) val[p][j] = get_int(); sort(val[p] + 1,val[p] + 1 + m); change(1,pre[p]); } else { memset(ans1,0,sizeof(ans1)); memset(ans2,0,sizeof(ans2)); int u,v; scan(u); scan(v); ask_ans1(1,pre[u],suf[u]); if(u != v) find_ans2(v,fa[u]); int ans = 0; for(int i = 0;i <= m;i ++) ans = max(ans,ans1[i] + ans2[m - i]); printf("%d\n",ans); } } fclose(stdin); fclose(stdout); return 0;}
- 【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP
- HDU上的专题训练(背包问题-线段树+树状数组+DP优化+网络流+字符匹配+最短路+矩阵乘法+搜索专题++)
- HDU上的专题训练(背包问题-线段树+树状数组+DP优化+网络流+字符匹配+最短路+矩阵
- hdu4751 最短路+背包dp
- 【BZOJ1706】[usaco2007 Nov]relays 奶牛接力跑【DP】【矩阵乘法】【限制最短路】
- 【BashuOJ3520】警察局长-最短路树+树上背包+概率DP
- 九度OJ 1407(线段树) 1408(DP) 1409(DP) 1410(DP) 1411(最短路)
- 【区间DP】矩阵乘法
- 【DP】矩阵链乘法
- 矩阵乘法优化DP
- dp 最优矩阵乘法
- hdu6185 dp+矩阵乘法
- 矩阵乘法优化DP
- zoj 3408 GAO 最短路 DP 布斯乘法
- 矩阵乘法求最短路
- BZOJ 1494 NOI2007 生成树计数 状压DP+矩阵乘法
- 【DP】最短路
- HDU2833-最短路+dp
- Score
- showModelDialog的使用(二):提供下载功能
- Centos 7.0 忘记密码,如何重置
- 【leetcode】225. Implement Stack using Queues
- 深入浅出 JavaScript 中的 this
- 【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP
- DSS 搭建手机流媒体服务器
- LeetCode *** 162. Find Peak Element
- 学习
- 括号匹配问题
- 小译UnityAPI-Awake,Start,Reset
- ffmpeg 命令行
- 子数组之和为零 lintcode
- 第 0000 题:将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似于微信未读信息数量那种提示效果。 类似于图中效果