博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

乱刷

(点击标题有传送QAQ)

D - Omkar and Medians

题目大意: 给定一个序列b描述$a_1..a_{2i-1}$的中位数, 请问是否存在一个序列a满足b的描述

做法: 每次向集合中添加两个元素, 产生的效果是: 让中位数变小1, 中位数不变, 中位数变大1(此处的1表示在集合中的相对大小), 可以发现$b_i$描述的元素肯定是a中的元素, 那么, 如果集合中比$b_i$小的数中的最大值不是$b_{i-1}$ 或者比$b_i$大的数中的最小值不是$b_{i-1}$, 那么这个a就是不存在的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 int main(){
18     #ifndef ONLINE_JUDGE
19         freopen("0.txt","w",stdout);
20     #endif
21     
22     int T=read();
23     while(T--){
24         int n=read();
25         vector<int> a(n);
26         for(int i=0; i<n; i++)
27             a[i]=read();
28 
29         set<int> s;
30         s.insert(a[0]);
31         bool flag = 1;
32         for(int i=1; i<n; i++){
33             if(a[i]==a[i-1])
34                 continue;
35             auto p = s.insert(a[i]).first;
36             if(*(a[i-1]>a[i]?next(p):prev(p))!=a[i-1]){
37                 flag=0;
38                 break;
39             }
40         }
41         puts(flag?"Yes":"No");
42     }
43 
44 }
View Code

 

 

E - Phoenix and Computers

题目大意: 一个长为n的全0字符串, 如果s[i-1]=1&&s[i+1]=1, 那么s[i]=1, 每次操作可以把某个0变成1, 现在求把它变成全1有多少种方案

做法: 设把字符串划分成k段, 每段通过一个0分开, 那么, 我们用dp[i][j]表示把前i个, 一共有j个1, s[i]=0的方案数, 那么$dp[i+k+1][j+k]=dp[i+k+1][j+k]+dp[i][j]*2^{k-1}*C_{j+k}^{k}$

其中$2^{k-1}$表示的是没有自动补全地填一段长为k的字符串的方案数, 而由于每一段是独立的, 只要相对顺序不变即可, $C_{j+k}^{k}$则表示这k次操作在全部j+k次操作里的位置

最后的答案是$\sum_{j=1}^n dp[n+1][j]$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 int main(){
18     #ifndef ONLINE_JUDGE
19         freopen("0.txt","w",stdout);
20     #endif
21     
22     int n=read(), mod=read();
23     vector<vector<int>>C(n+2, vector<int>(n+2, 0));
24     C[0][0]=1;
25     for(int i=1; i<=n+1; i++){
26         C[i][0]=1;
27         for(int j=1; j<=i; j++)
28             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
29     }
30 
31     vector<vector<int>> dp(n+2, vector<int>(n+1, 0));
32     auto power = [&](int a, int b){
33         int res=1;
34         for(; b; a=1ll*a*a%mod, b>>=1)
35             if(b&1)
36                 res=1ll*a*res%mod;
37         return res;
38     };
39 
40     dp[0][0]=1;
41        for(int i=0; i<=n; i++){
42            for(int j=0; j<=i; j++)
43                for(int k=1; i+k+1<=n+1; k++)
44                    dp[i+k+1][j+k]=(dp[i+k+1][j+k]
45                        +1ll*C[j+k][k]*power(2, k-1)%mod*dp[i][j]%mod)%mod;
46        }
47 
48        int ans = 0;
49        for(int i=1; i<=n; i++)
50            ans=(ans+dp[n+1][i])%mod;
51        printf("%d\n", ans);
52 }
View Code

 

 

D - Flipping Range 

题目大意: 给一个序列a和一个集合B, 每次可以从集合B中挑一个元素x, 然后将a中长为x的连续子段进行一次区间取反, 求最大的$\sum a_i$

做法: 考虑B中两个元素x, y, 进行一次x和一次y之后, 等价于对一段$|x-y|$的区间取反, 也就是说, 我们所有的取反操作等价于对$gcd(b_1...b_m)$取反

考虑a序列可以到哪些状态。 我们设一段字符串$s=0....0/1...1$ 其中1表示乘1, 0表示乘-1, 我们发现对一段长为g的区间取反, 设$f(c)=Xor s[i], i\ mod\ g==c$, 那么对于所有的f(0..g-1), 都是相等的。

也就是说, 所有的位置是独立的, 只要s‘’对应的f相同, 那么s可以通过一系列操作变为s'

于是可以通过dp来解决这个问题

dp[i][j]表示前i位, 所有x==i%g的位置的和的最大值

那么有

dp[i][j]=max(dp[i-g][j^1]-a[i], dp[i-g][j]+a[i])

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 inline void solve(){
18     
19     int n=read(), m=read();
20     vector<int> a(n);
21     for(int i=0; i<n; i++)
22         a[i]=read();
23     int g=read();
24     for(int i=1; i<m; i++)
25         g=__gcd(g, (int)read());
26 
27     vector<vector<ll>> dp(n, vector<ll>(2));
28     for(int i=0; i<n; i++){
29         if(i<g)
30             dp[i][0]=a[i],
31             dp[i][1]=-a[i];
32         else
33             dp[i][0]=max(dp[i-g][0]+a[i], dp[i-g][1]-a[i]),
34             dp[i][1]=max(dp[i-g][1]+a[i], dp[i-g][0]-a[i]);
35     }
36     ll res[2]={0};
37     for(int i=n-g; i<n; i++)
38         for(int j=0; j<2; j++)
39             res[j]+=dp[i][j];
40     printf("%lld\n", max(res[0], res[1]));
41 
42 }
43 
44 int main(){
45     #ifndef ONLINE_JUDGE
46         freopen("0.txt","w",stdout);
47     #endif
48     
49     int T=read();
50     while(T--)
51         solve();
52 
53 }
View Code

 

 

 

E. Spanning Tree Queries

题目大意: 给定一张n个点, m条边的无向图, 询问k次, 每次给个x, 问你把所有边权改成|wi-x|后, 整个图的最小生成树边权和多少, 询问之间独立 k 10^7

做法: 其实应该都做过这类题: 给定一条数轴, 选一个点使得$\sum |a_i-x|$最小

实际上是差不多的, 但是不同的是这里的点需要保持连通性

可以发现除了边权对应的点之外, 他们的中点就是一个最小生成树的突变点, 所以我们可以把所有的边权, 以及他们的中点取出来, 然后先把这些点对应的最小生成树跑出来, 然后, 对于x的答案, 从我们已经确定答案的点中, 找一个离他最近的点, 两者生成树等价, 原因跟上面的例题是一样的, (在中点中间随意取, \sum |a_i-x|不变)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ll long long
  4 #define pb push_back
  5 
  6 inline ll read() {
  7     ll x = 0, f = 1; char ch = getchar();
  8     for(; ch < '0' || ch>'9'; ch = getchar())
  9         if(ch == '-') f = -f;
 10     for(; ch >= '0' && ch <= '9'; ch = getchar())
 11         x = x * 10 + ch - '0';
 12     return x * f;
 13 }
 14 
 15 #define ln endl
 16 
 17 struct node {
 18     int u, v, w, id;
 19 }a[305];
 20 
 21 int f[55];
 22 int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
 23 
 24 int main(){
 25     #ifndef ONLINE_JUDGE
 26         freopen("0.txt","w",stdout);
 27     #endif
 28     
 29     vector<int> v;
 30     int n=read(), m=read();
 31     for(int i=1; i<=m; i++)
 32         a[i].u=read(), a[i].v=read(), a[i].w=read(), a[i].id=i;
 33 
 34     for(int i=1; i<=m; i++){
 35         v.pb(a[i].w);
 36         for(int j=1; j<=m; j++)
 37             v.pb((a[i].w+a[j].w)/2-1),
 38             v.pb((a[i].w+a[j].w)/2),
 39             v.pb((a[i].w+a[j].w)/2+1);
 40     }
 41 
 42     sort(v.begin(), v.end());
 43     v.erase(unique(v.begin(), v.end()), v.end());
 44 
 45     vector<vector<int>> st(v.size());
 46 
 47     for(int k=0; k<v.size(); k++){
 48         int x=v[k];
 49         sort(a+1, a+m+1, [&](node a, node b){
 50             return abs(a.w-x)<abs(b.w-x);
 51         });
 52         
 53         for(int i=1; i<=n; i++)
 54             f[i]=i;
 55         for(int i=1; i<=m; i++){
 56             int u=find(a[i].u), v=find(a[i].v);
 57             if(u!=v)
 58                 f[v]=u, st[k].pb(a[i].id);
 59         }
 60     }
 61 
 62     sort(a+1, a+m+1, [](node a, node b){
 63         return a.id<b.id;
 64     });
 65 
 66     auto calc = [&](int x){
 67         int l=0, r=v.size()-1, pos=0;
 68         while(l<=r){//第一个比x大的位置
 69             int mid=l+r>>1;
 70             if(v[mid]<=x)
 71                 pos=mid, l=mid+1;
 72             else
 73                 r=mid-1;
 74         }
 75 
 76         if(pos+1<v.size()&&v[pos+1]-x<x-v[pos])
 77             ++pos;
 78 
 79         ll ans = 0;
 80         for(int e: st[pos])
 81             ans = ans + abs(x-a[e].w);
 82         return ans;
 83 
 84     };
 85 
 86     int p=read(), k=read(), a=read(), b=read(), c=read(), x;
 87     ll ans=0;
 88 
 89     for(int i=1; i<=k; i++){
 90         if(i<=p)
 91             x=read();
 92         else
 93             x=(1ll*x*a%c+b)%c;
 94         ll tmp = calc(x);
 95         // printf("%d %lld\n", x, tmp);
 96         ans^=tmp;
 97     }
 98 
 99     printf("%lld\n", ans);
100 }
View Code

 

 

E2. Distance Tree (hard version)

题目大意: 给定一棵以1为根的有根树, 边权为1, 询问在树中添加一条边权为i的边之后(i:1~n), 所有点到1的距离的最大值最小是多少

做法: 首先, 添加的这条边肯定是连接(1, u)的, 设边权i对应的答案为ans, 那么, 对于每一棵子树u, 找到两个节点v1, v2使得他们是子树u的一条直径(depv1>ans, depv2>ans), 那么, 最好的方法就是连在v1和v2的中点上, 此时能让这条直径对半分, 使得这一棵子树内的点到1的最大值最小。 当i对应的答案是ans时, ans满足ans>=最大深度, 或者满足$\lceil \frac{g[ans]}{2} \rceil + x \leq ans$其中g[ans]表示的是, 存在d[v]>ans的子树内, 最远的两个点的距离。 可以发现随着ans的增大, g[ans]是减小的。 所以g[ans]可以$O(N)/O(Nlog_2N)$求

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 inline void solve(){
18     int n=read();
19 
20     vector<vector<int>> e(n);
21     vector<int> f(n, 0), d(n, 0), g(n, 0);
22     for(int i=1; i<n; i++){
23         int u=read(), v=read();
24         --u; --v;
25         e[u].pb(v);
26         e[v].pb(u);
27     }
28 
29     std::function<void(int, int)> dfs = [&](int u, int fa){
30         int mx1=d[u], mx2=d[u];//最深的两个点
31         g[u]=d[u];
32         for(int v: e[u]){
33             if(v==fa) 
34                 continue;
35             d[v]=d[u]+1;
36             dfs(v, u);
37             if(g[v]>mx1)
38                 mx2=mx1, mx1=g[v];
39             else if(g[v]>mx2)
40                 mx2=g[v];
41             g[u]=max(g[u], g[v]);//最深的点
42         }
43         int tmp = min(mx1, mx2)-1, //找>ans的两个点
44             dis = mx1+mx2-2*d[u];
45         if(~tmp)
46             f[tmp] = max(f[tmp], dis);
47     };
48 
49     dfs(0, -1);
50 
51     for(int i=n-2; ~i; i--)
52         f[i] = max(f[i+1], f[i]);
53 
54     int ans = 0;
55     for(int i=1; i<=n; i++){
56         while(ans<g[0]&&((f[ans]+1)/2+i)>ans)
57             ++ans;
58         printf("%d ", ans);
59     }
60     puts("");
61 
62     // puts("");
63 }
64 
65 int main(){
66     #ifndef ONLINE_JUDGE
67         freopen("0.txt","w",stdout);
68     #endif
69     
70     int T=read();
71     while(T--)
72         solve();
73 
74 }
View Code

 

 

C - Almost Sorted

  $d\leq 5$, 所以我们发现 , 对于每个$i(a_i==-1)$, 对于$j, j<i$, p[j]==p[i]的范围在$[i-2d, i-1]$中

  我们设dp[i][S]表示第i个位置, [i-2d, i]选数的状态为S的方案数

  可以发先, 转移的话, 直接把S>>1就是当前的状态了, S中的从低往高第j位表示i-j-d有没有填

  然后就直接转移

  效率$O(N2^{2d+1})$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 const int mod = 998244353;
18 inline void add(int &x, int y){
19     x+=y;
20     if(x>=mod) x-=mod;
21     if(x<0) x+=mod;
22 }
23 
24 int main(){
25     #ifndef ONLINE_JUDGE
26         freopen("0.txt","w",stdout);
27     #endif
28     // putchar('\x43'); putchar('\n');
29     // printf("%d\n", sizeof("\x43"));
30     // printf("%d\n", sizeof(""));
31     // putchar('\x4');
32     int n, d;
33     n=read(); d=read();
34     vector<int> a(n), vis(n,0);
35     int dp[505][1<<11]={0};
36     for(int i=0; i<n; i++){
37         a[i]=read();
38         if(a[i]==-1);
39         else 
40             vis[--a[i]]=1;
41     }
42     //对每个-1, 开dp[i][j]存一下, 当前这一位往前2d位选了j
43     //可以发现只需要存最近的10个单位内的-1
44     //存一下最近10个-1用了什么数字
45        if(a[0]!=-1)
46            dp[0][0]=1;
47        else{
48            for(int j=d; j>=-d; j--){
49                int x=j;
50                if(j>=n)
51                    continue;
52                if(j<0) break;
53                if(!vis[x])
54                    dp[0][(1<<(j+d))]=1;
55            }
56        }
57        for(int i=1; i<n; i++)
58            for(int j=0; j<(1<<11); j++){//枚举前面几个-1的状态
59                int k = j>>1;
60                if(a[i]!=-1)
61                    add(dp[i][k],dp[i-1][j]);
62                else{
63                    for(int p=d; p>=-d; p--){
64                        int x=p+d;//第几位
65                        // printf("%d\n", x);
66                        if(i+p>=n)//值为多少
67                            continue;
68                        if(i+p<0)
69                            break;
70                        if(((k>>x)&1)||vis[i+p]) continue;
71                        //这个值, 之前的2d个没用过, 且, 没有确定的值用过
72                        add(dp[i][k^(1<<x)], dp[i-1][j]);
73                    }
74                }
75            }
76        int ans = 0;
77        for(int j=0; j<(1<<11); j++)
78            // if(dp[n-1][j])
79                // printf("%d %d\n", j, dp[n-1][j]);
80            add(ans, dp[n-1][j]);
81        printf("%d\n", ans);
82 }
View Code

 

H - Hidden Fortress

  交互题, 首先我们设左下右上$(x, y), (a, b)$

  那么询问$(1, 1), (1, 10^9), (10^9, 1)$, 可以得到三组独立方程

  $$x-1+y-1=d1(1)\\ x-1+10^9-b=d2(2)\\ 10^9-a+y-1=d3(3)\\$$

  显然, 有4个未知数, 3组方程是不够的  

  此时, 如果我们把, x二分出来, 那么, 这三组方程就够了

  如何二分, 考虑$(1, i)$的答案

  很明显他是一条$[1, x]$递减, $[x, \frac{x+a}{2}]$递增, $[\frac{x+a}{2}, a]$递减, $[a, 10^9]$递增的折线, 而且斜率为$\pm 1$

  所以, 你拿一条$y=d1+1-x$的曲线去拟合他, 跟他重合的部分, 只会有$[1, x]$这一部分, 那么二分就可以了

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 const int mod = 1e9+7;
18 inline void add(int &x, int y){
19     x+=y;
20     if(x>=mod) x-=mod;
21     if(x<0) x+=mod;
22 }
23 
24 inline int calc(int x, int y, int a, int b){
25     return abs(a-x)+abs(b-y);
26 }
27 
28 inline int dist(int x, int y){
29     return min({calc(x, y, 2, 3), calc(x, y, 4, 5), calc(x, y, 2, 5), calc(x, y, 4, 3)});
30 }
31 
32 int main(){
33     #ifndef ONLINE_JUDGE
34         // freopen("0.txt","w",stdout);
35     #endif
36     printf("? 1 1\n");
37     fflush(stdout);
38     int d1=dist(1, 1);
39     printf("%d\n", dist(1, 1));
40     scanf("%d", &d1);
41 
42     printf("? 1000000000 1\n");
43     fflush(stdout);
44     int d2=dist(1000000000, 1);
45     printf("%d\n", dist(1000000000, 1));
46     scanf("%d", &d2);
47 
48     printf("? 1 1000000000\n");
49     fflush(stdout);
50     int d3=dist(1, 1000000000);
51     printf("%d\n", dist(1, 1000000000));
52     scanf("%d", &d3);
53 
54     int l=1, r=1e9, x=0;
55     while(l<=r){
56         int mid=(l+r)>>1;
57         printf("? %d 1\n", mid);
58         fflush(stdout);
59         int dis=dist(mid, 1);
60         printf("%d\n", dist(mid, 1));
61         scanf("%d", &dis);
62         if(d1-dis==mid-1)
63             l=mid+1, x=mid;
64         else
65             r=mid-1;
66     }
67     int y, a, b;
68     // printf("??? %d %d %d\n", d1, d2, d3);
69     y=d1-x+2; a=1000000000-d2+y-1; b=1000000000+x-d3-1;
70     printf("! %d %d %d %d\n", x, y, a, b);
71     fflush(stdout);
72 }
View Code

 

B - Frog Traveler

  有这么一个结论, 在答案序列中, 一个点只会经过一次, 要不然就陷入循环了

  那么, 我们用set维护一下某个点有没有跳到过, 然后bfs一下, 这样每个点只会入队1次, 出队1次

  记录一下, 每个点的跳板在哪里, 从哪里来即可, 即i(从哪里来)->i-a[i](跳板)->i-a[i]+b[i-a[i]]

  当然也可以线段树优化最短路建图把这题冲了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back

inline ll read() {
    ll x = 0, f = 1; char ch = getchar();
    for(; ch < '0' || ch>'9'; ch = getchar())
        if(ch == '-') f = -f;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        x = x * 10 + ch - '0';
    return x * f;
}

#define ln endl

const int mod = 1e9+7;
inline void add(int &x, int y){
    x+=y;
    if(x>=mod) x-=mod;
    if(x<0) x+=mod;
}

const int N=3e5+5;
int n, a[N], b[N], vis[N], pre[N], from[N];

int main(){
    #ifndef ONLINE_JUDGE
        freopen("0.txt","w",stdout);
    #endif
    n=read();
    for(int i=1; i<=n; i++)
        a[i]=read();
    for(int i=1; i<=n; i++)
        b[i]=read();
    for(int i=0; i<=n; i++)
        vis[i]=-1;
    queue<int> q; vis[n]=0; q.push(n);
    set<int> s;
    for(int i=0; i<n; i++)
        s.insert(i);
    while(!q.empty()){
        // puts("qwq");
        int now = q.front(); q.pop();
        int l=now-a[now], r=now;
        set<int>::iterator L=s.lower_bound(l), R=s.upper_bound(r);
        //能跳到这个区间内的任何一个点
        for(set<int>::iterator i=L; i!=R; i++){
            // printf("%d->%d\n", now, *i);
            if(vis[*i+b[*i]]==-1){
                vis[*i+b[*i]]=vis[now]+1;
                pre[*i+b[*i]]=now;
                from[*i+b[*i]]=*i;
                q.push(*i+b[*i]);
            }
        }
        s.erase(L, R);
    }
    // for(int i=0; i<n; i++)
        // printf("%d ", vis[i]);
    if(vis[0]==-1)
        return puts("-1"), 0;
    int x=0;
    vector<int> ans;
    while(x!=n)
        ans.pb(from[x]), x=pre[x];
    reverse(ans.begin(), ans.end());
    printf("%u\n", ans.size());
    for(int x:ans)
        printf("%d ", x);
    putchar('\n');
}
View Code

 

C - Optimal Insertion 

  可以有这么一个结论: b是按递增顺序插入a的

  假如不满足, 设b[i]<b[i+1], 但是插入的位置在i+1之后

  对于一个位置pos, 在x插入的贡献是, $[1,pos)$中比x大的+$[pos, n]$中比x小的

  考虑b[i]插入到pos1, b[i+1]插入到pos2, 假设pos1>pos2, 那么对于第一部分, 比b[i+1]大的肯定比b[i]大, 对于第二部分, 比b[i]小的肯定比b[i+1]小, 而且还多了一对(b[i+1], b[i])

  此时我们把他们翻转过来, 势必能够造成, 答案的减少。

  设dp[v][x]表示v插入到x前面的代价, 这个值=[1, x)中比v大的+[x, n]中比v小的

  当我们查询到b[i]的时候, 把a序列中比b[i]大的当做1, 把a序列中比b[i]小的当做0

  可以发现, 1的数量只会减少, 0的数量只会增加

  那么, 我们只需要设一开始都是1, 然后从a的最小值开始往a的最大值跳, 把1改成0即可

  我们考虑把1改成0的贡献, 我们把x这个位置改成了0, 对于[1, x), 他们的后面多了一个0, 则dp值均增加1

  对于[x, n], 他们前面少了一个1, 则dp值均减少1

  这个可以用线段树方便的维护,  然后只需要查询在$dp[i], i\in [1, n]$的最小值即可, 对于插入到最后, 他的值就是a中比他大的数的个数, 因为需要求一下a的逆序对, 所以可以顺便统计一下

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back

inline ll read() {
    ll x = 0, f = 1; char ch = getchar();
    for(; ch < '0' || ch>'9'; ch = getchar())
        if(ch == '-') f = -f;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        x = x * 10 + ch - '0';
    return x * f;
}

#define ln endl

const int mod = 1e9+7;
inline void add(int &x, int y){
    x+=y;
    if(x>=mod) x-=mod;
    if(x<0) x+=mod;
}

const int N=1e6+5;
int n, m, a[N], b[N], id[N];

int t[N<<2], lzy[N<<2];

void up(int rt){
    t[rt]=min(t[rt<<1], t[rt<<1|1]);
}

void build(int l, int r, int rt){
    lzy[rt]=0;
    if(l==r){
        t[rt]=l-1; return;
    }
    int mid=(l+r)>>1;
    build(l, mid, rt<<1);
    build(mid+1, r, rt<<1|1);
    up(rt);
}

void down(int rt){
    if(lzy[rt]){
        t[rt<<1]+=lzy[rt];
        t[rt<<1|1]+=lzy[rt];

        lzy[rt<<1]+=lzy[rt];
        lzy[rt<<1|1]+=lzy[rt];

        lzy[rt]=0;
    }
}

void add(int L, int R, int v, int l, int r, int rt){
    if(L<=l&&r<=R){
        t[rt]+=v; lzy[rt]+=v;
        return;
    }
    down(rt);
    int mid = (l+r)>>1;
    if(L<=mid)
        add(L, R, v, l, mid, rt<<1);
    if(R>mid)
        add(L, R, v, mid+1, r, rt<<1|1);
    up(rt);
}

vector<int> c;
int bit[N<<1];
inline int lowbit(int x){return x&-x;}
inline void add(int x){for(;x<=c.size();x+=lowbit(x)) bit[x]++;}
inline int ask(int x){int res=0; for(;x;x-=lowbit(x)) res+=bit[x]; return res;}

int main(){
    #ifndef ONLINE_JUDGE
        freopen("0.txt","w",stdout);
    #endif
    int T=read();
    while(T--){
        n=read(); m=read();
        c.clear();
        build(1, n, 1);
        for(int i=1; i<=n; i++)
            a[i]=read(), c.push_back(a[i]);
        for(int i=1; i<=n; i++)
            id[i]=i;
        sort(id+1, id+n+1, [&](int x, int y){
            return a[x]<a[y];
        });
        for(int i=1; i<=m; i++)
            b[i]=read(), c.push_back(b[i]);
        sort(c.begin(), c.end());
        c.erase(unique(c.begin(), c.end()), c.end());
        for(int i=1; i<=c.size(); i++)
            bit[i]=0;
        int now = 0;
        ll ans = 0;
        for(int i=1; i<=n; i++)
            a[i]=lower_bound(c.begin(), c.end(), a[i])-c.begin()+1,
            ans=ans+ask(c.size())-ask(a[i]),
            add(a[i]);
        for(int i=1; i<=m; i++)
            b[i]=lower_bound(c.begin(), c.end(), b[i])-c.begin()+1;
        sort(b+1, b+m+1);
        vector<int> v;
        //dp[i]表示在i之前插入x的代价
        //对于dp[n+1], 则为1..n中比i大的数
        for(int i=1; i<=m; i++){
            while(now+1<=n&&a[id[now+1]]<=b[i]){
                int x = id[++now];
                if(a[x]==b[i])
                    v.pb(x);
                else
                    add(1, x, 1, 1, n, 1);//多了一个比自己大的
                if(x+1<=n)
                    add(x+1, n, -1, 1, n, 1);//少了一个比自己小的
            }
            ans = ans + min(t[1], ask(c.size())-ask(b[i]));
            if(b[i]!=b[i+1]){
                for(int x: v)
                    add(1, x, 1, 1, n, 1);
                v.clear();
            }
        }
        printf("%lld\n", ans);
    }
}
View Code

 

C - Extreme Extension

  首先考虑f(l, r)怎么计算, 考虑贪心, 从r开始往前, 如果a[r-1]>a[r], 那么a[r-1]至少要拆成$\lceil \frac{a[r-1]}{a[r]} \rceil$个数, 贪心地让最小的那个数最大, 则最小的那个数为$\lfloor \frac{a[r-1]}{\lceil \frac{a[r-1]}{a[r]} \rceil} \rfloor$(因为要让这若干个数尽量的相同, 所以只能给每个数都按下取整分配)

  然后, 我们知道了一段f(l, r)怎么计算之后, 就可以开始dp了, dp[i][x]表示以i为左端点, 最小值为x的所有区间的值的和, 令g[i][x]表示以i为左端点,   最小值为x的区间的数量。

  可以推出

  dp[i][$\lfloor \frac{a[i]}{\lceil \frac{a[i]}{x} \rceil} \rfloor$] = $\sum_x$ dp[i+1][x] + g[i+1][x]*($\lceil \frac{a[r-1]}{a[r]} \rceil -1)$

  g[i][$\lfloor \frac{a[i]}{\lceil \frac{a[i]}{x} \rceil} \rfloor$] = $\sum_x$ g[i+1][x]

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 
 6 inline ll read() {
 7     ll x = 0, f = 1; char ch = getchar();
 8     for(; ch < '0' || ch>'9'; ch = getchar())
 9         if(ch == '-') f = -f;
10     for(; ch >= '0' && ch <= '9'; ch = getchar())
11         x = x * 10 + ch - '0';
12     return x * f;
13 }
14 
15 #define ln endl
16 
17 const int N=1e5+5;
18 const int mod = 998244353;
19 inline void add(int &x, int y){
20     x+=y;
21     if(x>=mod) x-=mod;
22     if(x<0) x+=mod;
23 }
24 
25 int dp[2][N], g[2][N], vis[N];
26 vector<int> v[2];
27 int n, a[N];
28 
29 int main(){
30     #ifndef ONLINE_JUDGE
31         // freopen("0.in","r",stdin);
32         freopen("0.txt","w",stdout);
33         double be = clock();
34     #endif
35     int T=read();
36     while(T--){
37         n=read();
38         for(int i=1; i<=n; i++)
39             a[i]=read();
40         for(int vv: v[n&1])
41             dp[n&1][vv]=g[n&1][vv]=0;
42         v[n&1].clear(); v[n&1].pb(a[n]);
43         dp[n&1][a[n]]=0; g[n&1][a[n]]=1;
44         int ans=0;
45         for(int i=n-1; i; i--){
46             for(int vv: v[i&1])
47                 dp[i&1][vv]=g[i&1][vv]=0;
48             v[i&1].clear(); v[i&1].pb(a[i]);
49             vis[a[i]]=1;
50             dp[i&1][a[i]]=0; g[i&1][a[i]]=1;
51             for(int vv: v[i&1^1]){
52                 int x=a[i]/((a[i]-1)/vv+1);
53                 if(!vis[x])
54                     vis[x]=1, v[i&1].pb(x);
55                 add(ans, (dp[i&1^1][vv]+1ll*g[i&1^1][vv]*((a[i]-1)/vv)%mod)%mod);
56                 add(dp[i&1][x], (dp[i&1^1][vv]+1ll*g[i&1^1][vv]*((a[i]-1)/vv)%mod)%mod);
57                 add(g[i&1][x], g[i&1^1][vv]);
58             }
59             for(int vv: v[i&1])
60                 vis[vv]=0;
61         }
62         printf("%d\n", ans);
63     }
64     #ifndef ONLINE_JUDGE
65         double en = clock();  
66         printf("Time: %.0lfms\n", en - be);
67         // fclose(stdin); fclose(stdout);
68     #endif
69 }
View Code
posted @ 2021-12-19 19:37  gllonkxc  阅读(41)  评论(0编辑  收藏  举报