• 乱刷


    (点击标题有传送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
  • 相关阅读:
    dom4j 解析 xml文件1
    java 简单的动态代理例子
    标识接口的作用 (转)
    JAVA servlet输出IE6下乱码
    java时间操作函数汇总
    IE支持getElementsByClassName方法
    女朋友问我 LB 是谁?
    人类高质量 Java 学习路线【一条龙版】
    程序员作图工具和技巧,你 get 了么?
    3 分钟了解 JSON Schema
  • 原文地址:https://www.cnblogs.com/gllonkxc/p/15708325.html
Copyright © 2020-2023  润新知