博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2016 Multi-University Training Contest 2
阅读量:5103 次
发布时间:2019-06-13

本文共 12099 字,大约阅读时间需要 40 分钟。

 

8/13

数学 A (CYD)

题意:

  给定一个向量,求他减去一个  α(>=0)乘以一个值为任意+1或-1的B向量后得到向量,求这个向量膜的最小值

思路:

  展开式子,当时最小,结果为

代码:

#include 
using namespace std;long long w,a,b,c,a2;long long gcd(long long x,long long y){ return y ? gcd(y,x%y) : x;}int main(){ int T; int n; int i,j,k; scanf("%d",&T); while(T--) { scanf("%d",&n); a=0,b=1ll*n,a2=0; for(i=1;i<=n;i++) { scanf("%I64d",&w); if(w<0) w=-w; a+=w; a2+=w*w; } a=a*a; c=gcd(a,b); a/=c; b/=c; a=a2*b-a; c=gcd(a,b); a/=c; b/=c; printf("%I64d/%I64d\n",a,b); } return 0;}

树形DP B (BH)

题意:

  有一棵树,每个节点有权值。对于每一个节点s,使最大,其中v1,v2,...,vm编号的点都是前一个点的祖先(不一定是父亲),opt是位操作。

思路:

  官方给出了一个很巧妙的做法,注意到而且又是位运算,然后因为是树,考虑树形DP。考虑dp[a][b],a表示的数的低8位,b表示数的高8位,dp意思是到当前状态下,下次要和a进行opt以及上一次opt的数是b的最优值。注意理解红字的含义,dp是交替的,对a转移,对b更新。我写的a和b与标程相反也是可以的,因为只要交替就可以,顺序无所。时间复杂度

代码:

#include 
using ll = long long;using uint = unsigned;const int MOD = 1e9 + 7;const int N = (1 << 16) + 5;char op[4];int n;int w[N];std::vector
edges[N];uint opt(uint a, uint b) { if (op[0] == 'A') return a & b; if (op[0] == 'O') return a | b; return a ^ b;}uint dp[1<<8][1<<8]; //dp[低8位][高8位]uint backup[N][1<<8];uint ans[N];template
T _max(T &a, T b) { if (a == -1 || a < b) a = b;}void DFS(int u) { uint val = 0; uint a = w[u] & 255, b = w[u] >> 8; for (int i=0; i<256; ++i) { if (dp[i][b] != -1) _max (val, dp[i][b] + opt (i, a)); } ans[u] = val + w[u]; std::copy (dp[a], dp[a] + 256, backup[u]); for (int i=0; i<256; ++i) { _max (dp[a][i], val + opt (i, b) * 256); } for (int v: edges[u]) { DFS (v); } //back up std::copy (backup[u], backup[u] + 256, dp[a]);}uint solve() { memset (dp, -1, sizeof (dp)); DFS (1); uint ret = 0; for (int i=1; i<=n; ++i) { ret = (ret + (ll) i * ans[i] % MOD) % MOD; } return ret;}int main() { int T; scanf ("%d", &T); while (T--) { scanf ("%d%s", &n, op); for (int i=1; i<=n; ++i) { scanf ("%u", w+i); edges[i].clear (); } for (int i=2; i<=n; ++i) { int x; scanf ("%d", &x); edges[x].push_back (i); } printf ("%u\n", solve ()); } return 0;}

不会 C 

归并树 D (BH)

题意:

  简单来说就是两个数列a和b,两种操作:

  1. 赋值[l, r]区间里a[i]为x

  2. 询问[l, r]区间多少个a[i]>=b[i]

思路:

  看了,强行看懂了。人家的题解写得已经很清楚了,不多说了。这题还有其他的做法,先学会了这一种。前提先知道归并树=归并排序+线段树(《挑战程序设计竞赛》P188),本题的关键是b数组不变,那么考虑第一操作的x对于b数组而言,现在有多少个数字<=x,用线段树维护。

代码:

#include 
const int N = 1e5 + 5;const int MOD = 1e9 + 7;int A, B;const int C = ~(1<<31), M = (1<<16)-1;int rnd(int last) { int a = (36969 + (last >> 3)) * (A & M) + (A >> 16); int b = (18000 + (last >> 3)) * (B & M) + (B >> 16); return (C & ((a << 16) + b)) % 1000000000;}int dat[20][N], sum[20][2];int tag[20][2];int lid[20][N], rid[20][N];int a[N], b[N];int ql, qr, x;int n, m;void upl(int p, int o) { sum[o][0] = p ? p - l + 1 : 0; tag[o][0] = -1;}void upr(int p, int o) { sum[o][1] = p ? p - l + 1 : 0; tag[o][1] = p;}void push_up(int o) { sum[o][0] = sum[o][1] = sum[o+1][0] + sum[o+1][1];}void push_down(int o) { if (tag[o][0]==-1 && tag[o][1]==-1) return ; if (tag[o][0] != -1) upl (lid[tag[o][0]], o+1); if (tag[o][1] != -1) upr (rid[tag[o][1]], o+1); tag[o][0] = tag[o][1] = -1;}void build(int l, int r, int o) { tag[o][0] = tag[o][1] = -1; if (l == r) { dat[o][l] = b[l]; sum[o][0] = sum[o][1] = a[l] >= b[l]; return ; } int mid = l + r >> 1; build (l, mid, o+1); build (mid+1, r, o+1); //merge int lp = l, rp = mid + 1, p = l; while (lp<=mid && rp<=r) { dat[o][p++] = dat[o+1][lp]
> 1, ret = 0; if (ql <= mid) modify (lid[p], l, mid, o+1); if (qr > mid) mofify (rid[p], mid+1, r, o+1); push_up (o);}int query(int p, int l, int r, int o) { if (ql <= l && r <= qr) { return sum[o][0]; } push_down (o); int mid = l + r >> 1, ret = 0; if (ql <= mid) ret += query (l, mid, o+1); if (qr > mid) ret += query (mid+1, r, o+1); return ret;}void solve() { build (1, n, 1); std::sort (b+1, b+1+n); int last = 0, ans = 0; for (int i=1; i<=m; ++i) { ql = rnd (last) % n + 1; qr = rnd (last) % n + 1; x = rnd (last) + 1; if (ql > qr) std::swap (ql, qr); if ((ql+qr+x) & 1) { int p = std::lower_bound (b+1, b+1+n, x) - b; modify (p, 1, n, 1); } else { int z = query (1, n, 1); ans = (ans + (ll) i * z % MOD) % MOD; last = 0; } } printf ("%d\n", ans);}int main() { int T; scanf ("%d", &T); while (T--) { scanf ("%d%d%d%d", &n, &m, &A, &B); for (int i=1; i<=n; ++i) scanf ("%d", a+i); for (int i=1; i<=n; ++i) scanf ("%d", b+i); solve (); } return 0;}

极角排序 E (BH)

题意:

  xjb推导之后发现就是求多少个子集共线。

思路:

  听说这题用极角排序的要不卡常数,要不卡精度。题解做法是gcd求斜率,因为输入的是整数。计算子集的方法自己看。

代码:

#include 
const double EPS = 1e-10;double PI = acos (-1.0);struct Point { int x, y; Point() {} Point(int x, int y) : x(x), y(y) {} bool operator < (const Point &rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); } Point operator - (const Point &rhs) const { return Point (x-rhs.x, y-rhs.y); } bool operator == (const Point &rhs) const { return x == rhs.x && y == rhs.y; } void reduce() { int g = std::__gcd (abs (x), abs (y)); if (g) { x /= g; y /= g; } } void read() { scanf ("%d%d", &x, &y); }};typedef long long ll;const int N = 1e3 + 5;const int MOD = 1e9 + 7;Point p[N], q[N];int pow_two[N];int n;void add_mod(int &a, int b) { a += b; if (a >= MOD) a -= MOD;}void init_pow() { pow_two[0] = 1; for (int i=1; i

点双连通分量 F (BH)

题意:

  有一个无向图G,每个点有权值。定义表示删除点i后的图,表示,其中,意思是删除点i后,每一个连通子图的权值乘积的和。

思路:

  先求点双连通分量,按照题解的做法,根据新点(bcc_cnt)和割顶建成新的图,然后树形DP计算:(v是u子树的点)。显然只有删除割顶时才会多出连通分量,那么先减去全部和,再加回多出来的分量。其他情况:不是割顶而且不是不是根节点,不会多出连通分量,所以只要除掉wi即可。

  官方题解写到还有CDQ+并查集的做法,已经把他去年出的类似的题做掉了,这题待补。

代码:

#include 
typedef long long ll;const int N = 1e5 + 5;const int M = 2e5 + 5;const int MOD = 1e9 + 7;int dfn[N];int dfs_clock, bcc_cnt;bool is_cut[N];std::vector
edges[N<<1], bcc[N<<1];int sta[N];int top;int w[N<<1], iw[N<<1];int n, m;int pow_mod(int x, int n) { int ret = 1; for (; n; n>>=1) { if (n & 1) ret = (ll) ret * x % MOD; x = (ll) x * x % MOD; } return ret;}int Tarjan(int u, int fa) { int lowu = dfn[u] = ++dfs_clock; int child = 0; sta[top++] = u; for (int v: edges[u]) { if (!dfn[v]) { child++; int lowv = Tarjan (v, u); lowu = std::min (lowu, lowv); if (lowv >= dfn[u]) { is_cut[u] = true; bcc[++bcc_cnt].clear (); w[bcc_cnt] = 1; for (; ;) { int x = sta[--top]; w[bcc_cnt] = (ll) w[bcc_cnt] * w[x] % MOD; bcc[bcc_cnt].push_back (x); if (x == v) break; } bcc[bcc_cnt].push_back (u); w[bcc_cnt] = (ll) w[bcc_cnt] * w[u] % MOD; } } else if (dfn[v] < dfn[u] && v != fa) { lowu = std::min (lowu, dfn[v]); } } if (fa < 0 && child == 1) is_cut[u] = false; return lowu;}void find_bcc() { memset (dfn, 0, sizeof (dfn)); memset (is_cut, false, sizeof (is_cut)); dfs_clock = top = 0; bcc_cnt = n; for (int i=1; i<=n; ++i) { if (!dfn[i]) Tarjan (i, -1); }}void add_mod(int &a, int b) { a += b; if (a >= MOD) a -= MOD; if (a < 0) a += MOD;}int fa[N<<1];int dp[N<<1], sum[N<<1], belong[N<<1];bool vis[N<<1];void DFS(int u, int pa, int o) { belong[u] = o; fa[u] = pa; vis[u] = true; dp[u] = w[u]; for (int v: edges[u]) { if (v == pa) continue; DFS (v, u, o); dp[u] = (ll) dp[u] * dp[v] % MOD; } if (u > n) { for (int v: bcc[u]) { belong[v] = o; vis[v] = true; } }}int solve() { find_bcc (); for (int i=1; i<=n; ++i) iw[i] = pow_mod (w[i], MOD - 2); for (int i=1; i<=bcc_cnt; ++i) { edges[i].clear (); } for (int i=n+1; i<=bcc_cnt; ++i) { for (int v: bcc[i]) { if (is_cut[v]) { edges[i].push_back (v); edges[v].push_back (i); w[i] = (ll) w[i] * iw[v] % MOD; } } } int sum = 0, ret = 0; memset (vis, false, sizeof (vis)); for (int i=n+1; i<=bcc_cnt; ++i) { if (!vis[i]) { DFS (i, -1, i); add_mod (sum, dp[i]); } } for (int i=1; i<=n; ++i) { if (!vis[i]) { DFS (i, -1, i); add_mod (sum, dp[i]); } } for (int i=1; i<=n; ++i) { int x = belong[i]; int tmp = sum; add_mod (sum, -dp[x]); if (!is_cut[i]) { if (i != belong[i]) add_mod (sum, (ll) dp[x] * iw[i] % MOD); } else { int tmp2 = (ll) dp[x] * pow_mod (dp[i], MOD - 2) % MOD; if (i != belong[i]) add_mod (sum, tmp2); for (int v: edges[i]) { if (v == fa[i]) continue; add_mod (sum, dp[v]); } } add_mod (ret, (ll) i * sum % MOD); sum = tmp; } return ret;}int main() { int T; scanf ("%d", &T); while (T--) { scanf ("%d%d", &n, &m); for (int i=1; i<=n; ++i) edges[i].clear (); for (int i=1; i<=n; ++i) { scanf ("%d", w+i); } for (int i=1; i<=m; ++i) { int u, v; scanf ("%d%d", &u, &v); edges[u].push_back (v); edges[v].push_back (u); } printf ("%d\n", solve ()); } return 0;}

不会 G 

不懂 H 

贪心 I (ZCJ)

题意:

  

思路:

  

代码:

不会 J 

贪心 K (BH)

题意:

  有若干个不同的字母,任意组合成若干个回文串,问某种组合使得其中最短的回文串长度最大。

思路:

  好好反思,比赛时紧张,没想好就写,一直WA也是因为没想到奇数的字母也可以分配出来看成偶数字母添加回文串长度,被CYD点醒后立马AC。吸取教训,1. 写代码是一定要想好再写(特别是贪心乱搞题);2. 迟迟不过题,不能三人同时开题,需要交换题目或者合力先争取过某一题。

代码:

#include 
const int INF = 0x3f3f3f3f;const int N = 1e5 + 5;int a[N];int main() { int n; int sum[2]; int T; scanf ("%d", &T); while (T--) { int ans = INF; scanf ("%d", &n); sum[0] = sum[1] = 0; int m = 0; for (int i=1; i<=n; ++i) { int x; scanf ("%d", &x); if (x & 1) { ans = std::min (ans, x); a[m++] = x; } else { sum[0] += x; } } if (m == 0) { ans = sum[0]; printf ("%d\n", ans); continue; } std::sort (a, a+m); int mn = a[0]; for (int i=1; i

DP+bitset L (BH)

题意:

  

思路:

  数据加强了,时限缩短一半,暴力很难跑过去了。标程都T了,好在铭神常数小,跑得快~。明天早上再看看

代码:

#include 
using LL = long long ;#define ALL(v) (v).begin(),(v).end()#define showtime printf("time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)const int N = 100000 + 5;const int M = 5000 + 5;int n,m;char s[N],t[M];std::bitset
bs[3],w[26];void work() { for (int i = 0; i < 3; ++ i) { bs[i].reset(); } for (int i = 0; i < 26; ++ i) { w[i].reset(); } for (int i = 0; i < n; ++ i) { w[s[i] - 'a'][i] = 1; } for (int i = 0; i < n; ++ i) { bs[0][i] = 1; } for (int i = 0; i < m; ++ i) { int a = t[i] - 'a'; bs[(i + 1) % 3] = bs[i % 3] & w[a] >> i; if (i > 0) { int b = t[i - 1] - 'a'; bs[(i + 1) % 3] |= bs[(i + 2) % 3] & w[a] >> i - 1 & w[b] >> i; } } for (int i = 0; i < n; ++ i) { if (bs[m % 3][i] == 1) putchar('1'); else putchar('0'); } puts("");}int main() { int cas; scanf("%d",&cas); while (cas--) { scanf("%d%d",&n,&m); scanf("%s%s",s,t); work(); }}

不会 M 

 

转载于:https://www.cnblogs.com/NEVERSTOPAC/p/5692901.html

你可能感兴趣的文章
SSM整合(spring mybatis)图书
查看>>
Linux学习笔记--终端命令
查看>>
关于电脑桌面图标消失并且右键无法点击的情况
查看>>
JAVA窗口2
查看>>
【Alpha】第八次Scrum meeting
查看>>
学习进度条11
查看>>
剑指offer之【树的子结构】
查看>>
Http协议中常用字段总结(不定时完善中)
查看>>
大道至简——第二章读后感
查看>>
线程的分离与结合
查看>>
混沌数学之Arnold模型
查看>>
判断一个数是偶数还是素数 做相应处理并排序输出
查看>>
进制转换问题
查看>>
Docker 容器的数据管理
查看>>
驱动相关Error
查看>>
补坑:Prufer 编码总结
查看>>
mysql5.4数据库安装_mysql数据库5.6.45安装后的配置(离线安装包版)
查看>>
Mysql 自动加锁_MYSQL事务 SELECT会自动加锁 及乐观锁
查看>>
mysql 表情字符集_mysql Emoji表情字符集转换
查看>>
iis看mysql连接数_关于IIS连接数和在线人数的详细说明_MySQL
查看>>