2015-2016 Northwestern European Regional Contest 训练总结 【5+2】【待补】

来源:互联网 发布:审美差异知乎 编辑:程序博客网 时间:2024/05/17 07:49

比赛通过题数:5

感觉配合还是有点问题, 不应该让cb一个人开K,他前一个半小时思路错了,后来才发现, 卡A的时候,应该一个人静静想想,另一个人去帮cb开K的..总结教训..


A.Assigning Workstations && BZOJ 4425




佩内洛普是新建立的超级计算机的管理员中的一员。 她的工作是分配工作站给到这里来运行他们的计算研究任务的研究人员。
佩内洛普非常懒惰,不喜欢为到达的研究者们解锁机器。 她可以从在她的办公桌远程解锁这些机器,但她并不觉得这卑贱的任务配得上她,所以她决定忽略安全指南偷偷懒。她可以直接地要求,研究者在他们离开时不用锁定自己的工作站,然后把未在使用且还在未锁定状态的工作站分配给新来的研究人员。 这样,她只需要为每一个工作站第一次被使用所属的研究员解锁工作站,这对佩内洛普的工作来说是一个巨大的改善。
不幸的是,如果一个工作站在未锁定且没被使用的状态下超过m分钟,会自动锁定自己,佩内洛普必须为使用它的下一个研究员再次打开它。 鉴于抵达和离开的研究人员的确切时间表,你可以告诉佩内洛普,要求研究者在离开时不锁定工作站最多可以使她节约多少次的解锁工作。你可以认为这儿总是有足够的可用工作站。
Input
一行两个整数n (1≤n≤300000) 研究员的数量n,以及 m (1≤m≤100000000) 工作站在未锁定且没被使用的状态下超过m分钟会自动锁定。
下面的n行,每一行两个整数a与s (1≤a,s≤100000000) 表示一个研究员在第a分钟时到达以及待了s分钟后离开。
Output
输出研究者在离开时不锁定工作站最多可以使她节约多少次解锁工作。


思路:按照左端点排序, 对于每个新进来的人,他肯定要么自己一台机器,要么跟前面某个人一台机器, 如果能跟前面的,肯定贪心选结束时间最早且跟他相差m以内的,让m以内的其他点有可能跟后面的人匹配。

比赛的时候最开始猜了一个右端点排序, 对于每个左端点贪心找前面结束时间m以内,跟他相距最远的,这样是有问题的, 因为按照结束时间排序, 开始的时间就影响不了什么了, 这样排序之后,对于每个人 他后面的那个人 可能不是紧跟着他来的那个人, 再下一个可能才是紧跟着他来的人, 这样就导致了可能更新完了, 紧跟着那个人要重开一台机器,而答案是要紧跟着那个人跟他一台机器。

这样按照左端点排序,就是按照时间顺序决定每个人该干什么

#include <bits/stdc++.h>using namespace std;int n, m, ans;priority_queue< int, vector< int >, greater< int > > q;pair< int, int > p[300005];int main(){scanf("%d%d", &n, &m);for (int i = 0; i < n; i++)scanf("%d%d", &p[i].first, &p[i].second);sort(p, p + n);for (int i = 0; i < n; i++)    {while (!q.empty() && q.top() + m < p[i].first)q.pop();if (q.empty() || q.top() > p[i].first)ans++;elseq.pop();q.push(p[i].first + p[i].second);}printf("%d\n", n - ans);return 0;}


D Debugging  && BZOJ 4428

你看中的调试器将不会在这件事上帮助你。有代码可以通过多种方式在调试与正式发布的间隙发生不同的行为,当出现这种情况,我们可能不得不求助于更原始的调试方式。
所以,你和你的printf现在在寻求一行导致该发布版本崩溃的代码。幸运的是:增加printf语句到程序里,既不会制造bug(仍然崩溃在同一原始的代码行),也没有影响执行时间(至少不显著)。 因此,即使在每行前加一个printf语句,运行程序,直到它崩溃,并检查最后打印行。
然而,它需要一些时间来添加每个printf语句到代码,并且该程序可以具有很多行。
因此,把一个printf语句在程序的中间或许是一个更好的计划,让它运行,观察它是否在加入行前崩溃,然后继续在代码的前一或后一半寻找。
不过话又说回来,运行程序可能需要很多时间,所以时效最优的策略可能是介于两者之间。
编写计算最坏情况下的最小时间来寻找崩溃行(无论它在哪里),认为你选择最优的加printf语句策略。
我们在5小时内发布新的版本,所以这个问题十分严重,需要尽快解决。

Input

输入包括一行三个整数:
n(1≤n≤10^6),代码行的数目;
r(1≤r≤10^9),编译和运行程序直到它崩溃的时间量;
p(1≤p≤10^9),增加单个的printf行所花费的时间。
您已经运行一次程序,因此已经知道它崩溃的地方。

Output

输出的最坏情况使用最优策略找到崩溃行的时间。

思路:显然,最坏的情况指的是出错地方一直处在最长的区间当中,为了使时间最少,我们每次都要尽可能的去等分区间.
设dp[n] 表示在最坏的情况下debug  n行代码所需要的最少的时间.那么有:
dp[n] = min((i-1)*p+dp[ceil(n/i)])+r;    
表示的是 : n行代码 分成i 块处理所需要的最少的时间.然后对均分之后区间最长的那个进行递归处理即可.
(i-1)p 是加 i-1行printf的时间.

这样的话复杂度应该是足够过这个题了,复杂度不是很好算啊 = =.

优化:
因为我们发现 ceil(n/i) 有很多重复的.而且取值相同的i都在连续的一段内,那么为了花费最小,我们肯定是取最小i
以下来自转载

我们就要推导下标啦。

总之就是要解这个方程

x、i、n为非负整数,已知n、i,求最小的x


ps : 下面这个式子是因为你随着分的块越多,i越大,那么这个i就是当前块, 最长区间为ceil(n/i) 。因为我们要跳过重复的,

所以要直接找到下一个块数 x.他们两个区间长度差1




有两种方法

第一种:看Claris博客,得到x = (n - 1) / ((n - 1) / i) + 1(这里的除均为整除)

第二种:

首先要知道


(具体数学里面有)

带入,得到


同乘上x


因为我们要求x的最小值,所以我们只看左边的不等式


因为x为非负整数,左边不一定是整数,那么x的最小值应该是



代码:

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e6 + 10;const ll INF = 1e18;ll dp[maxn], r, p;ll ceil(int a, int b){    return (a+b-1)/b;}ll dfs(int n){    if(n <= 1)        return 0;    if(dp[n] != -1)        return dp[n];    ll ans = (ll)(n-1)*p + r;    for(int i = 2; i < n; i = ceil(n, (ceil(n,i)-1)))    {        ans = min(ans, dfs(ceil(n, i)) + (ll)(i-1)*p+r);    }    return dp[n] = ans;}int main(){    int n;    while(~scanf("%d%lld%lld", &n, &r, &p))    {        memset(dp, -1, sizeof(dp));        printf("%lld\n", dfs(n));    }    return 0;}

E Elementary Math

题意很简单, 给你n对数,每个数可以有+ - * 三中操作,现在要求这n对数计算过后的结果任意两个不一样,问你是否有可能
.可能输出任意一种方案,否则输出impossible


考虑这个题目 每一对数只有可能是三种结果. 可以把这n对数抽象成n个点,每个点对应右边三个点右边.然后就是最后看
能否找到一个二分图匹配, 离散化一下,水题;


#include<iostream>  #include<cstdio>  #include<cstring>  #include<algorithm>  #include<vector>  #include<set>  #include<map>  using namespace std;  typedef long long ll;  const int maxn = 1e4+5;  struct node  {      ll x, y, v;      char sb;  }a[maxn];  int n;  vector<int> g[maxn];  int match[maxn], vis[maxn];  ll val[maxn];    bool dfs(int x)  {      int len = g[x].size();      for(int i = 0; i < len; i++)      {          int to = g[x][i];          if(!vis[to])          {              vis[to] = 1;              if(!match[to] || dfs(match[to]))              {                  match[to] = x;                  return 1;              }          }      }      return 0;  }    int Hungary()  {      int res = 0;      memset(match, 0, sizeof(match));      for(int i = 1; i <= n; i++)      {          memset(vis, 0, sizeof(vis));          res += dfs(i);      }      return res;  }    int main(void)  {        while(cin >> n)      {          int cnt = 1;          map<ll, int> m;          set<int> s;          s.clear(), m.clear();          for(int i = 1 ; i <= n; i++)              g[i].clear();          for(int i = 1; i <= n; i++)          {              scanf("%I64d%I64d", &a[i].x, &a[i].y);              ll add = a[i].x+a[i].y;              ll sub = a[i].x-a[i].y;              ll fac = a[i].x*a[i].y;              if(s.find(add) == s.end()) m[add] = cnt++, s.insert(add);              if(s.find(sub) == s.end()) m[sub] = cnt++, s.insert(sub);              if(s.find(fac) == s.end()) m[fac] = cnt++, s.insert(fac);              g[i].push_back(m[add]);              g[i].push_back(m[sub]);              g[i].push_back(m[fac]);              val[m[add]] = add;              val[m[sub]] = sub;              val[m[fac]] = fac;          }          int ans = Hungary();  //        cout << ans << endl;          if(ans != n) puts("impossible");          else          {              for(int i = 1; i < cnt; i++)                  if(match[i])                      a[match[i]].v = val[i];              for(int i = 1; i <= n; i++)              {                  if(a[i].v == a[i].x+a[i].y) a[i].sb = '+';                  if(a[i].v == a[i].x-a[i].y) a[i].sb = '-';                  if(a[i].v == a[i].x*a[i].y) a[i].sb = '*';              }              for(int i = 1; i <= n; i++)                  printf("%I64d %c %I64d = %I64d\n", a[i].x, a[i].sb, a[i].y, a[i].v);          }      }      return 0;  }  

G Guessing Camels && BZOJ 4430 BIT求偏序对 OR cdq分治+BIT


aap,Jan和Thijs在摩洛哥参加完ACMICPC2015全球总决赛后去沙漠进行了一次旅行。旅行包括了骑骆驼。
在骑完回来之后,他们的向导邀请他们在晚上去看一场盛大的骆驼赛跑。他们骑的骆驼也会参加,而赌比赛的结果是
一个习惯。其中一个最有趣的赌涉及猜测完成比赛的骆驼的完整名单。这个赌可以为你赢得最多的钱,因为它也是最
难猜对的。
Jaap,Jan和Thijs已经下了注,但比赛要在一个小时后才开始,所以他们觉得很无聊。他们开始想知道有多少对骆驼
他们赌了同样的顺序。如果在他们三人的猜测名单中,骆驼c在骆驼d前,就意味着c和d在他们的名单中有相同的顺序
。你能帮他们计算有多少对骆驼是这样的吗?


题目大意:给出三个1~n的排列,求有多少个数对,在这三个排列中的相对位置相同。

思路:

考虑容斥:

  答案 = 总对数 - 不满足条件的对数.

  现在就是考虑怎么求不满足的对数.这里巧妙的利用了BIT

  我们可以发现不满足偏序性质的这一对,一定是在某两个排列里位置是相同的,只有一个和他们的位置不相同,那么我们可以对每两个排列求一次不满足题意的偏序对的个数.但是这样算的话 对于那个排列里位置不同的数对,在另两个排列被算了两次,所以把最后的答案除以2就是最终的结果了.

  求偏序对的话就是对于两个排列x,y. 先预处理处理x 中每一个x[i]的位置i,然后从后往前遍历 y[i].

那么关于一个点对x,y,若它们在两个排列中 
____y__u______x____k 
_______x___________y 
这样从后向前扫第二个排列,扫到y的时候在第一个排列y的位置+1,然后扫到x的时候查询第一个排列x位置的前缀和,前缀和即为不满足条件的对数 
所以就对扫到的每一个数都这样做就行了


即 

  将第二个排列中所有偏序对的位置固定下来作为一个标准,那么从后往前遍历第二个排列时,假设遍历到第i个,那么第i个后面所有的数都在他的后面了,由于我们把i后面的数都在第一个排列中的对应位置+1,那么现在我们求一下第二个排列中的第i个数在第一个排列中对应位置前面有多少个数,就可以求出不满足的偏序对个数了,好好体会..

get到了一种求两个序列之间的“逆序对”的方法

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;typedef long long ll;const int maxn = 2e5 + 10;const int Mod = 1e9 + 7;int c[maxn], pos[maxn], A[maxn], B[maxn], C[maxn], n;int lowbit(int x){    return x & -x;}void add(int x, int d){    while(x < maxn)    {        c[x] += d;        x += lowbit(x);    }}int sum(int x){    int ans = 0;    while(x)    {        ans += c[x];        x -= lowbit(x);    }    return ans;}ll solve(int *x, int *y){    for(int i = 1; i <= n; i++)        pos[x[i]] = i, c[i] = 0;  //把第一个序列每个值所在的位置映射到pos上    ll ans = 0;    for(int i = n; i >= 1; i--)    {        ans += sum(pos[y[i]]);  // 这都是在第二个序列后面的, 如果在第一个序列有在前面的,说明他们的相对序列不一样        add(pos[y[i]], 1);    }    return ans;}int main(){    scanf("%d", &n);    for(int i = 1; i <= n; i++) scanf("%d", &A[i]);    for(int i = 1; i <= n; i++) scanf("%d", &B[i]);    for(int i = 1; i <= n; i++) scanf("%d", &C[i]);    ll ans = (ll)n*(n-1)/2;    ans -= (solve(A,B) + solve(A, C) + solve(B,C)) / 2;    printf("%lld\n", ans);    return 0;}



Identifying Map Tiles

J Jumbled Communication


给你 x^(x<<1) 的结果 让你求x,观察到x最大255 所以反过来逆推打个表就好


[cpp] view plain copy
  1. #include <iostream>  
  2. #include <cstring>  
  3. #include <cstdio>  
  4. using namespace std;  
  5. const int maxn = 1e5 + 5;  
  6. int a[maxn], ans[maxn];  
  7. void init()  
  8. {  
  9.     for(int i = 0; i <= 255; i++)  
  10.         a[i] = (i^(i<<1))%256, ans[a[i]] = i;  
  11. }  
  12. int main()  
  13. {  
  14.     int n;  
  15.     init();  
  16.     while(~scanf("%d", &n))  
  17.     {  
  18.         int x;  
  19.         for(int i = 1; i <= n; i++)  
  20.         {  
  21.             scanf("%d", &x);  
  22.             printf("%d%c", ans[x], i == n ? '\n' : ' ');  
  23.         }  
  24.     }  
  25.     return 0;  
  26. }  

K -Kitchen Combinatorics 大模拟- -

[cpp] view plain copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<algorithm>  
  5. #include<vector>  
  6. #include<set>  
  7. #include<map>  
  8. using namespace std;  
  9. typedef long long ll;  
  10. const int maxn = 2e3+5;  
  11. const ll INF = 1e18;  
  12. int r, s, m, d, n, flag;  
  13. ll b[maxn], a[maxn][maxn], cnt1[maxn], cnt2[maxn], cnt3[maxn];  
  14. vector<int> g[maxn];  
  15.   
  16. ll mul(ll a, ll b)  
  17. {  
  18.     if(!a || !b) return 0;  
  19.     if(INF/a < b)  
  20.     {  
  21.         flag = 1;  
  22.         return 1;  
  23.     }  
  24.     return a*b;  
  25. }  
  26.   
  27.   
  28. int main(void)  
  29. {  
  30.     while(cin >> r >> s >> m >> d >> n)  
  31.     {  
  32.         for(int i = 0; i < maxn; i++)  
  33.             g[i].clear();  
  34.         for(int i = 1; i <= r; i++)  
  35.             scanf("%I64d", &b[i]);  
  36.         for(int i = 1; i <= s; i++)  
  37.         {  
  38.             scanf("%I64d", &a[i][0]);  
  39.             for(int j = 1; j <= a[i][0]; j++)  
  40.                 scanf("%I64d", &a[i][j]);  
  41.         }  
  42.         for(int i = s+1; i <= s+m; i++)  
  43.         {  
  44.             scanf("%I64d", &a[i][0]);  
  45.             for(int j = 1; j <= a[i][0]; j++)  
  46.                 scanf("%I64d", &a[i][j]);  
  47.         }  
  48.         for(int i = s+m+1; i <= s+m+d; i++)  
  49.         {  
  50.             scanf("%I64d", &a[i][0]);  
  51.             for(int j = 1; j <= a[i][0]; j++)  
  52.                 scanf("%I64d", &a[i][j]);  
  53.         }  
  54.         for(int i = 1; i <= n; i++)  
  55.         {  
  56.             int x, y;  
  57.             scanf("%d%d", &x, &y);  
  58.             g[x].push_back(y);  
  59.             g[y].push_back(x);  
  60.         }  
  61.         ll ans = 0, inf = 0;  
  62.         set<int> ss1;  
  63.         set<int>::iterator it;  
  64.         for(int i = 1; i <= s; i++)  
  65.         {  
  66.             ss1.clear();  
  67.             ll tmp = 1;  
  68.             flag = 0;  
  69.             memset(cnt1, 0, sizeof(cnt1));  
  70.             ll t = 1;  
  71.             bool have = 0;  
  72.             for(int ii = 1; ii <= a[i][0]; ii++)  
  73.             {  
  74.                 have = 1;  
  75.                 if(!cnt1[a[i][ii]]) t = mul(t, b[a[i][ii]]);  
  76. //                    t *= b[a[i][ii]];  
  77. //                else t += 1;  
  78.                 cnt1[a[i][ii]]++;  
  79.                 if(t > INF || t < 0) flag = 1;  
  80.             }  
  81.             if(!have) t = 0;  
  82.             int len = g[i].size();  
  83.             for(int ii = 0; ii < len; ii++)  
  84.                 ss1.insert(g[i][ii]);  
  85. //            cout << t << endl;  
  86. //            tmp *= t;  
  87.             tmp = mul(tmp, t);  
  88.             if(tmp > INF || tmp < 0) flag = 1;  
  89.             if(!tmp) continue;  
  90. //            if(i == 1) cout << '*' << tmp << endl;  
  91.             for(int j = s+1; j <= s+m; j++)  
  92.             {  
  93.                 ll tmp2 = tmp;  
  94.                 set<int> ss2; ss2.clear();  
  95.                 for(it = ss1.begin(); it != ss1.end(); it++)  
  96.                     ss2.insert(*it);  
  97.                 it = ss2.lower_bound(j);  
  98.                 if(it != ss2.end() && *it == j) continue;  
  99.                 t = 1, have = 0;  
  100.                 for(int z = 1; z < maxn; z++)  
  101.                     cnt2[z] = cnt1[z];  
  102.                 for(int jj = 1; jj <= a[j][0]; jj++)  
  103.                 {  
  104. //                    cout << i << ' ' <<  j << ' ' << jj << ' ' << a[j][jj] << endl;  
  105.                     have = 1;  
  106.                     if(!cnt2[a[j][jj]]) t = mul(t, b[a[j][jj]]);  
  107. //                        t *= b[a[j][jj]];  
  108. //                    else t += 1;  
  109.                     cnt2[a[j][jj]]++;  
  110.                     if(t > INF || t < 0) flag = 1;  
  111.                 }  
  112. //                cout << have << endl;  
  113. //                if(i == 1) cout << tmp << ' ' << t << endl;  
  114.                 if(!have) t = 0;  
  115.                 int len =  g[j].size();  
  116.                 for(int jj = 0; jj < len; jj++)  
  117.                     ss2.insert(g[j][jj]);  
  118. //                tmp2 *= t;  
  119.                 tmp2 = mul(tmp2, t);  
  120. //                cout << tmp2 << endl;  
  121. //                    cout << "****" << t << endl;  
  122.                 if(tmp2 > INF || tmp2 < 0) flag = 1;  
  123.                 if(!tmp2) continue;  
  124.   
  125. //                if(j == 4) cout << tmp << endl;  
  126.                 for(int k = s+m+1; k <= s+m+d; k++)  
  127.                 {  
  128.                     set<int> ss3;  
  129.                     ss3.clear();  
  130.                     for(it = ss2.begin(); it != ss2.end(); it++)  
  131.                         ss3.insert(*it);  
  132.                     it = ss3.lower_bound(k);  
  133.                     if(it != ss3.end() && *it == k) continue;  
  134.                     t = 1, have = 0;  
  135.                     ll tmp3 = tmp2;  
  136.                     for(int z = 1; z < maxn; z++)  
  137.                         cnt3[z] = cnt2[z];  
  138.                     for(int kk = 1; kk <= a[k][0]; kk++)  
  139.                     {  
  140.                         have = 1;  
  141.                         if(!cnt3[a[k][kk]])  
  142.                             t = mul(t, b[a[k][kk]]);  
  143. //                            t *= b[a[k][kk]];  
  144. //                        else t += 1;  
  145.                         cnt3[a[k][kk]]++;  
  146.                         if(t > INF || t < 0) flag = 1;  
  147.                     }  
  148. //                    cout << tmp3 << ' ' << t << ' ' << flag << endl;  
  149.                     if(!have) t = 0;  
  150. //                    tmp3 *= t;  
  151.                     tmp3 = mul(tmp3, t);  
  152.                     if(tmp3 > INF || tmp3 < 0) flag = 1;  
  153.                     if(!tmp3) continue;  
  154.                     if(flag == 1) inf = 1;  
  155.                     else  
  156.                     {  
  157. //                        cout << i << ' ' << j << ' ' << k << ' ' << tmp3 << endl;  
  158.                         ans += tmp3;  
  159. //                        cout << "***" << ans << endl;  
  160. //                        for(int z = 1; z <= r; z++)  
  161. //                            if(cnt3[z] == 3) ans += b[z];  
  162.                         if(ans > INF || ans < 0) inf = 1;  
  163.                     }  
  164.                 }  
  165.             }  
  166.         }  
  167.         if(inf) puts("too many");  
  168.         else printf("%I64d\n", ans);  
  169.     }  
  170.     return 0;  
  171. }  


阅读全文
0 0
原创粉丝点击