2016 Multi-University Training Contest 2
8/13
2016 Multi-University Training Contest 2
官方题解
数学 A Acperience(CYD)
题意:
给定一个向量,求他减去一个 α(>=0)乘以一个值为任意+1或-1的B向量后得到向量,求这个向量膜的最小值
思路:
展开式子,当时最小,结果为。
代码:
#include <bits/stdc++.h>
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 Born Slippy(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 <bits/stdc++.h> 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<int> 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<typename T>
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;
}
归并树 D Differencia(BH)
题意:
简单来说就是两个数列a和b,两种操作:
1. 赋值[l, r]区间里a[i]为x
2. 询问[l, r]区间多少个a[i]>=b[i]
思路:
看了Claris的代码,强行看懂了。人家的题解写得已经很清楚了,不多说了。这题还有其他的做法,先学会了这一种。前提先知道归并树=归并排序+线段树(《挑战程序设计竞赛》P188),本题的关键是b数组不变,那么考虑第一操作的x对于b数组而言,现在有多少个数字<=x,用线段树维护。
代码:
#include <bits/stdc++.h> 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]<dat[o+1][rp] ? dat[o+1][lp++] : dat[o+1][rp++];
}
while (lp <= mid) dat[o][p++] = dat[o+1][lp++];
while (rp <= r) dat[o][p++] = dat[o+1][rp++]; //rank
lp = l; rp = mid + 1;
for (int i=l; i<=r; ++i) {
while (lp<=mid && dat[o+1][lp]<=dat[o][i]) lp++;
while (rp<=r && dat[o+1][rp]<=dat[i][i]) rp++;
lid[o][i] = lp - 1; rid[o][i] = rp - 1;
if (lid[o][i] < l) lid[o][i] = 0;
if (rid[o][i] <= mid) rid[o][i] = 0;
} push_up (o);
} void modify(int p, int l, int r, int o) {
if (ql <= l && r <= qr) { }
push_down (o);
int mid = l + r >> 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 Eureka(BH)
题意:
xjb推导之后发现就是求多少个子集共线。
思路:
听说这题用极角排序的要不卡常数,要不卡精度。题解做法是gcd求斜率,因为输入的是整数。计算子集的方法自己看。
代码:
#include <bits/stdc++.h> 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<N; ++i) {
pow_two[i] = (ll) pow_two[i-1] * 2 % MOD;
}
} int solve() {
std::sort (p, p+n);
int ret = 0;
for (int i=0; i<n; ++i) {
int m = 0, cnt = 0;
for (int j=i+1; j<n; ++j) {
if (p[i] == p[j]) ++cnt;
else q[m++] = p[j] - p[i];
}
for (int j=0; j<m; ++j) {
q[j].reduce ();
}
std::sort (q, q+m);
add_mod (ret, pow_two[cnt] - 1);
for (int x=0, y; x<m; x=y) {
for (y=x; y<m && q[x] == q[y]; ++y);
add_mod (ret, (ll) pow_two[cnt] * (pow_two[y-x]-1) % MOD);
}
}
return ret;
} int main() {
init_pow ();
int T;
scanf ("%d", &T);
while (T--) {
scanf ("%d", &n);
for (int i=0; i<n; ++i) {
p[i].read ();
}
printf ("%d\n", solve ());
}
return 0;
}
点双连通分量 F Fantasia(BH)
题意:
有一个无向图G,每个点有权值。定义表示删除点i后的图,表示,其中,意思是删除点i后,每一个连通子图的权值乘积的和。
思路:
先求点双连通分量,按照题解的做法,根据新点(bcc_cnt)和割顶建成新的图,然后树形DP计算:(v是u子树的点)。显然只有删除割顶时才会多出连通分量,那么先减去全部和,再加回多出来的分量。其他情况:不是割顶而且不是不是根节点,不会多出连通分量,所以只要除掉wi即可。
官方题解写到还有CDQ+并查集的做法,已经把他去年出的类似的题做掉了,这题待补。
代码:
#include <bits/stdc++.h> 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<int> 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 Glorious Brilliance
不懂 H Helter Skelter
hdu 5741 Helter Skelter 官方题解做法的详细证明
贪心 I It's All In The Mind(ZCJ)
题意:
思路:
代码:
不会 J Join The Future
贪心 K Keep On Movin(BH)
题意:
有若干个不同的字母,任意组合成若干个回文串,问某种组合使得其中最短的回文串长度最大。
思路:
好好反思,比赛时紧张,没想好就写,一直WA也是因为没想到奇数的字母也可以分配出来看成偶数字母添加回文串长度,被CYD点醒后立马AC。吸取教训,1. 写代码是一定要想好再写(特别是贪心乱搞题);2. 迟迟不过题,不能三人同时开题,需要交换题目或者合力先争取过某一题。
代码:
#include <bits/stdc++.h> 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<m; ++i) {
int tmp = a[i] - mn;
a[i] = mn;
sum[0] += tmp;
} sum[0] /= 2;
ans = mn + sum[0] / m * 2;
printf ("%d\n", ans);
}
return 0;
}
DP+bitset L La Vie en rose(BH)
题意:
思路:
数据加强了,时限缩短一半,暴力很难跑过去了。标程都T了,好在铭神常数小,跑得快~。明天早上再看看
代码:
#include <bits/stdc++.h>
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<N> 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 Memento Mori
2016 Multi-University Training Contest 2的更多相关文章
- 2016 Al-Baath University Training Camp Contest-1
2016 Al-Baath University Training Camp Contest-1 A题:http://codeforces.com/gym/101028/problem/A 题意:比赛 ...
- 2016 Al-Baath University Training Camp Contest-1 E
Description ACM-SCPC-2017 is approaching every university is trying to do its best in order to be th ...
- 2016 Al-Baath University Training Camp Contest-1 A
Description Tourist likes competitive programming and he has his own Codeforces account. He particip ...
- 2016 Al-Baath University Training Camp Contest-1 J
Description X is fighting beasts in the forest, in order to have a better chance to survive he's gon ...
- 2016 Al-Baath University Training Camp Contest-1 I
Description It is raining again! Youssef really forgot that there is a chance of rain in March, so h ...
- 2016 Al-Baath University Training Camp Contest-1 H
Description You've possibly heard about 'The Endless River'. However, if not, we are introducing it ...
- 2016 Al-Baath University Training Camp Contest-1 G
Description The forces of evil are about to disappear since our hero is now on top on the tower of e ...
- 2016 Al-Baath University Training Camp Contest-1 F
Description Zaid has two words, a of length between 4 and 1000 and b of length 4 exactly. The word a ...
- 2016 Al-Baath University Training Camp Contest-1 D
Description X is well known artist, no one knows the secrete behind the beautiful paintings of X exc ...
- 2016 Al-Baath University Training Camp Contest-1 C
Description Rami went back from school and he had an easy homework about bitwise operations (and,or, ...
随机推荐
- <<< java异常The import java.util cannot be resolved
异常:The import java.util cannot be resolved 原因:这是由于你的项目buildpath不对 解决方案:右键项目-------buildpath--------最 ...
- <<< 入侵网站类提权注入教程
---------------------------------------入侵类教程-------------------------------------------------------- ...
- git push如何至两个git仓库
分别有仓库 A(github),B(JAE 的 git),本机为C. 假设以 a 仓库作为最终的使用仓库, b为发布仓库.分支都为 dev 第一步,增加远程仓库 git remote add orig ...
- IBatis存储过程返回值
<parameterMaps> <parameterMap id="delVersionBagInfoParam" class="DelVersionB ...
- 10月25日下午PHP静态、抽象、接口
多态(运行多态)概念:当父类引用指向子类实例,由于子类里面对父类的方法进行了重写,父类引用在调用该方法的时候表现出的不同状态.条件:1.必须发生在继承下2.必须重写父类方法3.父类引用调用该方法 如果 ...
- Scene
Unity 中场景切换 http://www.cnphp6.com/archives/62868 场景管理插件Scene Manager http://blog.csdn.net/onerain88/ ...
- Problem to be sovled
Given an array A of N integers, we draw N discs in a 2D plane such that the I-th disc is centered on ...
- Codeforces 699D Fix a Tree 并查集
原题:http://codeforces.com/contest/699/problem/D 题目中所描述的从属关系,可以看作是一个一个块,可以用并查集来维护这个森林.这些从属关系中会有两种环,第一种 ...
- CentOS防火墙iptables的配置方法详解
CentOS系统也是基于linux中的它的防火墙其实就是iptables了,下面我来介绍在CentOS防火墙iptables的配置教程,希望此教程对各位朋友会有所帮助. iptables是与Linux ...
- 安装配置LDAP遇到的问题
问题1:安装完启动ldap服务报错: ldap: unrecognized service? 原因在于新版的openldap将服务名改为了slapd,使用service slapd start即可启动 ...