10.2 广州集训 Day1

平衡(balance,1s,128M)

【题目描述】

P同学总共有k根火柴,分别放在摆成一列的n个火柴盒内,保证k是n的倍数。P同学想要每个火柴盒都有相同数目的火柴,每次他可以从一个火柴盒中拿一根火柴放到相邻的火柴盒中。他想知道他最少要移动多少次。

【输入格式】

从balance.in中读入。

第一行一个整数n,表示火柴盒数。

第二行n个整数,表示第i个火柴盒内有根火柴。

【输出格式】

输出到balance.out中。

一行一个整数,表示最少要移动多少次。

【样例一输入】

6

1 6 2 5 3 7

【样例一输出】

12

【子任务】

30%数据,1≤n≤100,0≤𝑎𝑖≤100

100%数据,1≤n≤50000,0≤𝑎𝑖≤10^9

Solution:

  模拟+贪心。

  没有什么其他的特别的东西,类似于NOIP2002的纸牌。

  我们可以先算出各盒火柴之和的sum,由p=sum/n可以得到每盒火柴的个数。

  然后从1到n寻找,如果火柴数大于0,就把多出的火柴移到下一个火柴盒。

  反之,如果小于0,就把下一个火柴盒的火柴移到这个火柴盒。

  值得一提的是,即使火柴变成负数也可以实现,因为我们可以保证存在一种合适的移动方法恒为非负。

 1 #include<cstdio>
 2 #define MAXN 50005
 3 #define LL long long
 4 using namespace std;
 5 int n,p=0;LL sum=0,cnt=0;
 6 LL a[MAXN];
 7 inline LL read(){
 8     int x=0,f=1;char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
10     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
11     return x*f;
12 }
13 LL Abs(LL x){return x>0?x:-x;}
14 int main(){
15     freopen("balance.in","r",stdin);
16     freopen("balance.out","w",stdout);
17     n=read();
18     for(int i=1;i<=n;i++) a[i]=read(),sum+=a[i];
19     p=sum/n;
20     for(int i=1;i<=n;i++) a[i]-=p;
21     //for(int i=1;i<=n;i++) printf("a[i]=%d\n",a[i]);
22     for(int i=1;i<=n;i++) {
23         if(!a[i]) continue;
24         else if(a[i]<0) cnt-=a[i];
25         else if(a[i]>0) cnt+=a[i];
26         a[i+1]+=a[i];
27     }
28     printf("%lld",Abs(cnt));
29     return 0;
30 }

 

 

  

                                                    道路(road,3s,512M)

 

    我们看见了一个由m行n列的1*1的格子组成的矩阵,每个格子(I,j)有对应的高度h[i][j]和初始的一个非负权值v[i][j].我们可以随便选择一个格子作为起点,然后在接下来的每一步当中,我们能且只能到达与当前格子有边相邻的四个格子中的高度不超过当前格子高度的格子,每当我们到达一个新格子(包括一开始选择的初始格子),我们就能得到该格子的权值分,然后该格子的权值就会等概率变成不比当前的权值大的一个非负权值。每一个格子在满足前面条件情况下,可以走任意多次。
    我们希望得到一个最大的期望权值和路径,并给出这个最大的期望权值和。

输入格式:
    第一行两个正整数表示m,n。
    接下来的m行,每行n个正整数,表示h[i][j].
    接下来的m行,每行n个非负整数,表示v[i][j].

输出格式:
    一行一个实数,表示所求的最大期望权值和。保留零位小数。

输入样例:
    1 3
    1 2 1
    1 2 3

输出样例:
    5

数据范围:
    对于30%的数据,保证n,m不超过100.
    对于另外20%的数据,保证不存在相同的高度的格子。
    对于100%的数据,保证n,m不超过1000.所有权值与高度不超过10^9.

 Solution:

  //留坑。下课填。

  我们首先可以通过设一个概率的dp:f[i]表示原价值为i重复走无限多次的期望总价值。

  然后可以得到以下式子:

  

   化简可得:

   

  然后,我们就把连通的同高度点当成一个点,然后对剩余的DAG做一个dp去维护最大权路径就可以了。

  //关于同一高度点的处理证明:相同高度点可以多次访问,即可以得到这一块的所有权值。权值非负可以得到贪心正确。

                特判可以保证网络流不会死循环。

Other:

   关于数学期望:

   关于更新题解:

   //其实以上都是口胡,等A了之后再次更新

  1 //std
  2 #include <iostream>
  3 #include <fstream>
  4 #include <cstring>
  5 #include <string>
  6 #include <cstdlib>
  7 #include <cstdio>
  8 #include <algorithm>
  9 #include <cmath>
 10 #include <bitset>
 11 #include <ctime>
 12 #include <map>
 13 #include <queue>
 14 #include <set>
 15 using namespace std;
 16 
 17 const int maxn=1000+15;
 18 const int dx[4]={-1,1,0,0};
 19 const int dy[4]={0,0,-1,1};
 20 vector <int> edge[maxn*maxn];
 21 int belonging[maxn][maxn],sum;
 22 long long keyvalue[maxn*maxn];
 23 int m,n,height[maxn][maxn],value[maxn][maxn];
 24 int lix[maxn*maxn],liy[maxn*maxn],hh,tt;
 25 long long f[maxn*maxn],ans;
 26 int bfs(int tx,int ty)
 27 {
 28     lix[hh=tt=1]=tx;
 29     liy[hh]=ty;
 30     keyvalue[sum]+=value[lix[hh]][liy[hh]];
 31     belonging[lix[hh]][liy[hh]]=sum;
 32     for (;hh<=tt;hh++)
 33     {
 34      int x=lix[hh],y=liy[hh];
 35      for (int i=0;i<4;i++)
 36      {
 37          int nx=x+dx[i],ny=y+dy[i];
 38          if (nx==0 || nx>m || ny==0 || ny>n || height[nx][ny]!=height[x][y] || belonging[nx][ny]!=0) continue;
 39          tt++;
 40          lix[tt]=nx;
 41          liy[tt]=ny;
 42          keyvalue[sum]+=value[nx][ny];
 43          belonging[nx][ny]=sum;
 44      }
 45     }
 46     if (tt>1) keyvalue[sum]*=2;
 47     return 0;
 48 }
 49 int getint()
 50 {
 51     char ch;
 52     for (ch=getchar();!isdigit(ch);ch=getchar());
 53     int s=0;
 54     for (;isdigit(ch);s=s*10+ch-'0',ch=getchar());
 55     return s;
 56 }
 57 
 58 long long dp(int x)
 59 {
 60      if (f[x]!=-1) return f[x];
 61      f[x]=0;
 62      for (int j=0;j<edge[x].size();j++)
 63          f[x]=max(f[x],dp(edge[x][j]));
 64      f[x]+=keyvalue[x];
 65      return f[x];
 66 }
 67 int main()
 68 {
 69     freopen("road.in","r",stdin);
 70     freopen("road.out","w",stdout);
 71     m=getint();n=getint();
 72     for (int i=1;i<=m;i++)
 73      for (int j=1;j<=n;j++)
 74       height[i][j]=getint();
 75     for (int i=1;i<=m;i++)
 76      for (int j=1;j<=n;j++)
 77       value[i][j]=getint();
 78     for (int i=1;i<=m;i++)
 79      for (int j=1;j<=n;j++)
 80       if (!belonging[i][j])
 81         {
 82             sum++;
 83             bfs(i,j);
 84         }
 85 //    return 0;
 86 //    int sss=0;
 87     for (int x=1;x<=m;x++)
 88      for (int y=1;y<=n;y++)
 89       for (int i=0;i<4;i++)
 90       {
 91           int nx=x+dx[i],ny=y+dy[i];
 92           if (nx==0 || nx>m || ny==0 || ny>n || height[nx][ny]>=height[x][y]) continue;
 93 //          sss++;
 94           edge[belonging[x][y]].push_back(belonging[nx][ny]);
 95       }
 96 ///    cout << sss << endl;
 97     memset(f,-1,sizeof(f));
 98     for (int i=1;i<=sum;i++) ans=max(ans,dp(i));
 99     cout << ans << endl;
100     return 0;
101 }

 

1 小豪的花园
程序文件名: flower.c/cpp/pas 输入文件: flower.in
输出文件: flower.out
时间限制: 3s
空间限制: 256Mb

 

题目描述
           小豪有一个花园,里面有n个花棚,编号1..n,每个花棚里有一定数量的花ai。小豪花园的路十分
神奇,可以使得任意两个花棚之间仅有一条最短路,即形成树结构,其中根节点是1号花棚。现在小豪 打算修缮一下他的花园,重新分配每个花棚里花的数量。为了能方便快捷地知道花园的情况,小豪现 在需要你的帮助。具体地说,小豪共有m个操作。操作有三种:
  1. 1 u k 表示如果一个花棚在以u号花棚为根的子树中,那么小豪会把这个花棚花的数量模k
  2. 2 u x 表示小豪将u号花棚花的数量变成x
  3. 3 u v 表示小豪询问从u号花棚走到v号花棚总共能看到的花的数量
         你能帮助小豪吗?

输入格式
  第一行有两个正整数n和m,代表花棚的数量和小豪操作的个数。
  接下来n − 1行,每行两个正整数u, v(1 ≤ u, v ≤ n, u 6= v),表示u号花棚与v号花棚直接有路相连。 下一行有n个非负整数a1, a2, ..., an,表示起始时每个花棚中花的数量。
  接下来m行,一行表示一个操作,格式如题所述。(可参考样例)

 

输出格式
  对于每个操作3输出一行一个整数表示结果。

 

样例

输入                    输出

8 7                     12 
1 2                      20
2 3                      9
2 4
2 5
1 6
6 7
6 8
1 2 3 4 5 6 7 8
3 3 6 1 2 3 2 2 4 3 4 8 2 3 11
1 1 5 3 3 7

 

数据范围
  • 对于20%的数据,n, m ≤ 2000
  • 另外10%的数据,所有花棚构成一条链,且无操作1
  • 另外20%的数据,无操作1
  • 对于100%的数据,n, m ≤ 100000, ai, x, k ≤ 10^8, k ≥ 1

 

Solution:

  这题是线段树(树状数组)、DFS序的综合应用。
  首先对于50%的数据,就是裸的树链剖分。也有时间复杂度少一个log n的做法,见下文。
  比较难搞的就是操作1,对子树取模,利用DFS序可以变成区间取模。
  区间取模问题的做法很简单,就是不断找区间的最大值,一个一个地进行取模操作直至最大值小于要模的值。可以证明,这样 做操作1的总时间复杂度不超过O((n + m) log x + m log n) (x 表示花的数量的最大值,下同)。注意到 一个数模了另一个数后要么不变,要么至多变为原来的一半,如此一个数(若没有修改)最多只能变 化log x次,且修改总共最多m次,因此所有数被修改的总次数不超过(n + m) log x。这个结果加上查找 最大值的时间即可得上述时间复杂度O((n + m) log x + m log n)。
  如此操作1和操作2都是单点修改,而操作3是路经询问。路经询问不好处理,可以通过差分变成子 树修改和单点询问。记f[u]表示u到根节点花的数量和,这样对于一个询问(u, v),设w是u和v的最近公 共祖先,询问的答案就是f[u] + f[v] − 2 ∗ f[w] + a[w];对于一个修改操作a[u] → x,则需要把u的子树 中所有f加上x − a[u]。利用DFS序,这两个操作用树状数组或者线段树即可完成。
  上述做法时间复杂度O((n + m) log n log x + m log n)。若无上段所述的转化,直接用树链剖分,时 间复杂度是O((n + m) log2 n log x + m log n),可能会超时。

 

Other:

  实际上树剖是可以过的,只是操作一写挂的我树剖只有20分...

  1 //std
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <algorithm>
  7 #include <ctime>
  8 #include <cmath>
  9 #include <vector>
 10 #include <iterator>
 11 using namespace std;
 12 typedef long long LL;
 13 
 14 const int maxn = 100005;
 15 
 16 int n, m, a[maxn];
 17 int q[maxn], fa[maxn], dep[maxn], size[maxn], st[maxn], ed[maxn], index[maxn];
 18 int Bits, lca[maxn][20];
 19 vector<int> edge[maxn];
 20 
 21 void Bfs(int root)
 22 {
 23     int h = 0, t = 0;
 24     fa[root] = 0; q[0] = root; dep[root] = 1;
 25     for (; h <= t; h++)
 26     {
 27         int u = q[h];
 28         for (int i = edge[u].size()-1; i >= 0; i--)
 29         // for (vector<int>::iterator i = edge[u].begin(); i != edge[u].end(); ++i)
 30         {
 31             // int v = *i;
 32             int v = edge[u][i];
 33             if (fa[u] == v) continue;
 34             q[++t] = v;
 35             fa[v] = u;
 36             dep[v] = dep[u] + 1;
 37         }
 38     }
 39     for (h = t; h >= 0; h--)
 40     {
 41         int u = q[h];
 42         size[u] = 1;
 43         for (int i = edge[u].size()-1; i >= 0; i--)
 44         // for (vector<int>::iterator i = edge[u].begin(); i != edge[u].end(); ++i)
 45         {
 46             // int v = *i;
 47             int v = edge[u][i];
 48             if (fa[u] == v) continue;
 49             size[u] += size[v];
 50         }
 51     }
 52     st[root] = 1; ed[root] = n;
 53     for (h = 0; h <= t; h++)
 54     {
 55         int u = q[h], t = st[u] + 1;
 56         for (int i = edge[u].size()-1; i >= 0; i--)
 57         // for (vector<int>::iterator i = edge[u].begin(); i != edge[u].end(); ++i)
 58         {
 59             // int v = *i;
 60             int v = edge[u][i];
 61             if (fa[u] == v) continue;
 62             st[v] = t;
 63             t += size[v];
 64             ed[v] = t - 1;
 65         }
 66     }
 67     for (int i = 1; i <= n; i++)
 68         index[ st[i] ] = i;
 69 }
 70 
 71 void LcaInit()
 72 {
 73     for (Bits = 0; (1 << (Bits + 1)) <= n; Bits++);
 74     for (int i = 1; i <= n; i++)
 75         lca[i][0] = fa[i];
 76     for (int j = 1; j <= Bits; j++)
 77         for (int i = 1; i <= n; i++)
 78             if (lca[i][j-1]) lca[i][j] = lca[ lca[i][j-1] ][j-1];
 79 }
 80 
 81 int Lca(int u, int v)
 82 {
 83     if (dep[v] > dep[u]) swap(u, v);
 84     for (int i = Bits; i >= 0; i--)
 85         if (dep[ lca[u][i] ] >= dep[v])
 86             u = lca[u][i];
 87     if (u == v) return u;
 88     for (int i = Bits; i >= 0; i--)
 89         if (lca[u][i] != lca[v][i])
 90             u = lca[u][i], v = lca[v][i];
 91     return fa[u];
 92 }
 93 
 94 namespace SegTreeMax
 95 {
 96     #define lson (root << 1)
 97     #define rson (root << 1 | 1)
 98 
 99     int tree[4*maxn], id[4*maxn];
100 
101     void Update(int root, int l, int r, int x, int val)
102     {
103         if (x < l || x > r) return;
104         if (l == x && r == x)
105         {
106             tree[root] = val;
107             id[root] = l;
108             return;
109         }
110         int mid = (l + r) >> 1;
111         Update(lson, l, mid, x, val);
112         Update(rson, mid+1, r, x, val);
113         if (tree[lson] > tree[rson])
114         {
115             tree[root] = tree[lson];
116             id[root] = id[lson];
117         } else {
118             tree[root] = tree[rson];
119             id[root] = id[rson];
120         }
121     }
122 
123     int Query(int root, int l, int r, int x, int y, int val)
124     {
125         if (y < l || x > r || tree[root] < val) return -1;
126         if (x <= l && r <= y) return id[root];
127         int mid = (l + r) >> 1;
128         int ret = Query(lson, l, mid, x, y, val);
129         if (ret != -1) return ret;
130         return Query(rson, mid+1, r, x, y, val);
131     }
132 }
133 
134 namespace TreeArray
135 {
136     LL tree[maxn];
137 
138     void Update(int x, int y, int val)
139     {
140         for (; x <= n; x += x & -x)
141             tree[x] += val;
142         for (y++; y <= n; y += y & -y)
143             tree[y] -= val;
144     }
145 
146     LL Query(int x)
147     {
148         LL sum = 0;
149         for (; x; x -= x & -x)
150             sum += tree[x];
151         return sum;
152     }
153 }
154 
155 int main()
156 {
157     freopen("flower.in", "r", stdin);
158     freopen("flower.out", "w", stdout);
159 
160     scanf("%d%d", &n, &m);
161     for (int i = 1; i < n; i++)
162     {
163         int u, v;
164         scanf("%d%d", &u, &v);
165         edge[u].push_back(v);
166         edge[v].push_back(u);
167         // Insert(u, v);
168     }
169     Bfs(1);
170     LcaInit();
171 
172     for (int i = 1; i <= n; i++)
173     {
174         int x;
175         scanf("%d", &x);
176         a[i] = x;
177         TreeArray::Update(st[i], ed[i], x);
178         SegTreeMax::Update(1, 1, n, st[i], x);
179     }
180     for (int i = 1; i <= m; i++)
181     {
182         int t, u, v, x;
183         scanf("%d", &t);
184         if (t == 1)
185         {
186             scanf("%d%d", &u, &x);
187             // cerr << x << endl;
188             while ((v = SegTreeMax::Query(1, 1, n, st[u], ed[u], x)) != -1)
189             {
190                 // cerr << v << endl;
191                 v = index[v];
192                 // cerr << v << endl;
193                 TreeArray::Update(st[v], ed[v], a[v] % x - a[v]);
194                 a[v] %= x;
195                 SegTreeMax::Update(1, 1, n, st[v], a[v]);
196             }
197         }
198         else if (t == 2)
199         {
200             scanf("%d%d", &v, &x);
201             TreeArray::Update(st[v], ed[v], x - a[v]);
202             a[v] = x;
203             SegTreeMax::Update(1, 1, n, st[v], a[v]);
204         }
205         else {
206             scanf("%d%d", &u, &v);
207             x = Lca(u, v);
208             LL uSum = TreeArray::Query(st[u]);
209             LL vSum = TreeArray::Query(st[v]);
210             LL xSum = TreeArray::Query(st[x]);
211             // cerr << u << ' ' << v << ' ' << x << endl;
212             // cerr << uSum << ' ' << vSum << ' ' << xSum << endl;
213             // cerr << a[x] << endl;
214             printf("%I64d\n", uSum + vSum - 2 * xSum + a[x]);
215         }
216     }
217 }

 

posted @ 2017-10-02 19:53  drizzly  阅读(545)  评论(0编辑  收藏  举报