zoukankan      html  css  js  c++  java
  • 3.26

    3.26-4.14

    CF-629D

    • 求上升子序列的最大和。O(n^2)会暴力,在查询的时候要用线段树维护
    • 因为权值是浮点数,故先离散化一下,设第 i 个位置的权值,从小到大排名为 id。那么dp转移中 $$d[i] = max(d[i],d[i] + d[j])$$ 其中$$j<i & id[j]<id[i]$$ ,故线段树结点区间[l,r]维护的是id = l 到 id = j 中的最大 dp值
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100000;
    double vol[N],r,h;
    int n,has[N],g[N],dp[N],tot;
    int c[N];
    vector<double> v;
    int getId(double a){
        return lower_bound(v.begin(),v.end(),a)-v.begin()+1;
    }
    struct Tree{
        int l,r;
        double data;
    }t[4*N];
    void build(int p,int l,int r){
        t[p].l = l;
        t[p].r = r;
        if(l==r){
            t[p].data = 0;return;
        }
        int mid = l+r>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        t[p].data = 0;
    }
    void change(int p,int x,double val){
        if(t[p].l == t[p].r && t[p].l == x){
            t[p].data = max(t[p].data,val);
            return ;
        }
        int mid = (t[p].l+t[p].r)>>1;
        if(x<=mid)change(p*2,x,val);
        else change(p*2+1,x,val);
        t[p].data = max(t[p*2].data,t[p*2+1].data);
    }
    double ask(int p,int l,int r){
        if(t[p].l>=l&&t[p].r<=r)return t[p].data;
        int mid = (t[p].l+t[p].r)>>1;
        double val = 0;
        if(l<=mid)val = max(val,ask(p*2,l,r));
        if(r>mid)val = max(val,ask(p*2+1,l,r));
        return val;
    }
    int main(){
        cin>>n;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&r,&h);
            vol[i] = acos(-1) * r * r * h;
            v.push_back(vol[i]);
        }
        sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
        build(1,1,n);
        double res = 0;
        for(int i=1;i<=n;i++){
            int id = getId(vol[i]);
            double now = ask(1,1,id-1);
            now = max(vol[i],vol[i]+now);
            res = max(res,now);
            change(1,id,now);
        }
        printf("%.10lf
    ",res);
        return 0;
    }
    
    • 辗转了很多次,惭愧,线段树做的题太少了

    法二:离散化+树状数组

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    const int inf = 0x3f3f3f3f;
    double vol[N],c[N];
    vector<double> v;
    int n,g[N];
    int getId(double res){
        return lower_bound(v.begin(),v.end(),res) - v.begin() + 1;
    }
    void add(int x,double y){
        c[x] = y;
        for(;x<=n;x+=x&-x)c[x] = max(c[x],y);
    }
    double ask(int x){
        double res = 0;
        for(;x;x-=x&-x)res = max(res,c[x]);
        return res;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            double r,h;
            scanf("%lf%lf",&r,&h);
            double vo = acos(-1) * r * r * h;
            vol[i] = vo;
            v.push_back(vo);
        }
        sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
        double res = 0;
        for(int i=1;i<=n;i++){
            int x = getId(vol[i]);
            double now = ask(x-1) + vol[i];
            add(x,now);
            res = max(res,now);
        }
        printf("%.10lf
    ",res);
        return 0;
    }
    

    CF-332B

    题意:给定长度为n的序列,寻找两段不相交的,长度为k的序列,使其权值和最大

    怎么都能做,其实类似单调队列的思想,只需要一个变量保存最优决策即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 200010;
    typedef long long ll;
    int n,k;
    ll a[N],sum[N];
    int main(){
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            sum[i] = sum[i-1]+a[i];
        }
        int pre = 1,l = 1,r = k+1,fi = 1,se = 1;
        ll res = 0;
        for(;r<=n-k+1;r++){
            if(sum[pre+k-1]-sum[pre-1] < sum[r-1]-sum[r-1-k])pre = r-k;
    
            if(sum[r+k-1]-sum[r-1]+sum[pre+k-1]-sum[pre-1] > res){
                res = sum[r+k-1]-sum[r-1]+sum[pre+k-1]-sum[pre-1];
                fi = pre;
                se = r;
            }
        }
        printf("%d %d
    ",fi,se);
        return 0;
    }
    

    CF-1043D

    • 题意:有间隔为k的n个点在数轴上,下标为 (1,k+1, 2*k+1, (n-1)*k+1) 首尾相接。设起点为s,步长为L,而现在只知道s距离最近的点的距离为a,和(s+L)距离最近的点的距离为b。问从s出发,第一次回到s走的最多和最少的步数。

    • 分析:设走x步回到起点,那么有(x*l = t * n * k) 即走了x步饶了 t 圈

      又因为x和t互质,即保证是第一次回到s,所以有 (x = {n * k over gcd(n*k, l)}) 。所以枚举所有可能的 l ,得到gcd的最大值和最小值即可。

    • l (l<k时)的取值只有四种情况,画图即可得知

      • (l = k-a-b)
      • (l = k+b-a)
      • (l = a+b)
      • (l = k+a-b)

      然后每一种情况又可以在原来的基础上多加 i 个k。总共4*n种 l

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll n,k,a,b;
    int main(){
        cin>>n>>k>>a>>b;
        ll mi = LONG_LONG_MAX;
        ll mx = 0;
        for(int i=0;i<n;i++){
            ll x1 = __gcd(n*k,k-a-b + i*k);
            ll x2 = __gcd(n*k,k+b-a + i*k);
            ll x3 = __gcd(n*k,a+b   + i*k);
            ll x4 = __gcd(n*k,k+a-b + i*k);
            mi = min(mi,min(x1,min(x2,min(x3,x4))));
            mx = max(mx,max(x1,max(x2,max(x3,x4))));
        }
        cout<<(n*k)/mx<<' '<<(n*k)/mi<<endl;
        return 0;
    }
    

    CF-620 E - New Year Tree (状态压缩+线段树)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 400010;
    typedef long long ll;
    int n,m,cnt,c[N],num[N],id[N];
    ll color[N];
    vector<int> v[N];
    void dfs(int x,int fa){
        num[x] = 1;
        color[++cnt] = 1ll<<c[x];
        id[x] = cnt;
        for(int i=0;i<v[x].size();i++){
            int y = v[x][i];
            if(y==fa)continue;
            dfs(y,x);
            num[x] += num[y];
        }
    }
    struct SegTree{
        int l,r;
        ll tag;
        ll data;
    }t[4*N];
    void build(int p,int l,int r){
        t[p].l = l;t[p].r = r;
        if(l==r){
            t[p].data = color[l];
            return ;
        }
        int mid = l+r>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        t[p].data = t[p*2].data | t[p*2+1].data;
    }
    void spread(int p){
        if(t[p].tag){
            t[p*2].data = t[p].data;
            t[p*2+1].data = t[p].data;
            t[p*2].tag = 1;
            t[p*2+1].tag = 1;
            t[p].tag = 0;
        }
    }
    void change(int p,int l,int r,int val){
        if(t[p].l>=l&&t[p].r<=r){
            t[p].data = 1ll<<val;
            t[p].tag = 1;
            return;
        }
        spread(p);
        int mid = t[p].l+t[p].r>>1;
        if(l<=mid)change(p*2,l,r,val);
        if(r>mid)change(p*2+1,l,r,val);
        t[p].data = t[p*2].data | t[p*2+1].data;
    }
    ll ask(int p,int l,int r){
        
        if(t[p].l>=l&&t[p].r<=r){
            return t[p].data;
        }
        spread(p);
        int mid = t[p].l+t[p].r>>1;
        ll res= 0;
        if(mid>=l)res |= ask(p*2,l,r);
        if(mid<r)res |= ask(p*2+1,l,r);
        return res;
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        for(int i=1;i<=n-1;i++){
            int x,y;scanf("%d%d",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        dfs(1,-1);
        build(1,1,n);
        for(int i=1;i<=m;i++){
            int k,x,y;
            scanf("%d%d",&k,&x);
            if(k==1){
                scanf("%d",&y);
                change(1,id[x],id[x]+num[x]-1,y);
            }
            else{
                ll res = ask(1,id[x],id[x]+num[x]-1);
                int ans = 0;
                while(res){if(res&1)ans++;res/=2;}
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    

    CF-1140 E - Palindrome-less Arrays

    题意:给定一个没有填完的序列,数值为-1表示你可以用 1~k 中的数字去覆盖它,求将该序列填充后,不存在长度为奇数的回文串的方案数

    分析:

    1. 使之不存在长度为奇数的回文串,只需要满足不存在长度为3的回文串即可。换句话说:(a[i] eq a[i+2]) 对所有的 (i) 成立。可以发现 i 为奇数与 i 为偶数是互不影响的。所以可以把它划分为两个串

      1. 一个串由 $a_1,a_3,a_5, dots $组成
      2. 另一个串由$ a_2,a_4,a_6,dots$ 组成
    2. 现在问题转化为了:给定一个序列,将其数值为-1的位置换为1~k中的数字,使得序列中两两相邻数字不同的方案数。不妨换个角度想,任何一组连续的 -1(长度可以为0或1),两边都只有四种情况

      1. 两边都没有数字(即整个串都是-1)
      2. 两边中有一边没有没有(只有整个串的左右两端有这种情况)
      3. 两边的数字相同
      4. 两边的数字不同

      另外我们可以发现,前两种情况可以由后两种情况推出来,所以只需预处理把 0~ (n/2)+1长度的-1串的方案数都预处理出来,问题就迎刃而解了。

    3. (d(i,j)) 表示长度为 (i) 的 -1 串,j 为0 表示两边数字相同,为1表示两边数字不同时的方案数,(d[0][0] = 0, d[0][1] = 1), 有转移方程:

      • (i) 为奇数
        • $d[i][0] = d[i/2][0]d[i/2][0] + (k-1)d[i/2][1]*d[i/2][1] $
        • (d[i][1] = d[i/2][0]*d[i/2][1]*2 + (k-2)*d[i/2][1]*d[i/2][1])
      • (i) 为偶数
        • (d[i][0] = (k-1)*d[i-1][1])
        • (d[i][1] = d[i-1][0] + (k-2)*d[i-1][1]%mod)

    对于 i 为奇数的情况,我们可以取出这个序列的中间位置 mid,当 -1 串两端数字相同且都等于 x 时,先假设mid数字与x相同,那就转换为了两个长度为 i/2,序列两端相同 的子问题,然后假设 mid 与 x不同,那么就有(k-1)种方法,可以同样转换成两个长度为 i/2 ,序列两端不同的子问题。当 -1 串两端数字不同时,同理。

    预处理d数组之后,就可以对我们之前分好的奇偶串做处理了。思路就是记录上一个不为-1的位置。然后最后做一下特判,就可以得到正确答案了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod = 998244353;
    ll d[100010][2];
    int a[100010],b[100010];
    ll n,k;
    ll solve(int *a,ll len){
        ll res = 1;
        ll last = 0;
        for(ll i=1;i<=len;i++){
            if(a[i] == -1)continue;
            else{
                if(i == 1){
                    last = i;continue;
                }
                if(last == 0){
                    res = res * (d[i-2][0] + (k-1)*d[i-2][1])%mod;
                }
                else{
                    if(a[i] == a[last]){
                        res = res * d[i-last-1][0]%mod;
                    }
                    else res = res * d[i-last-1][1]%mod;
                }
                last = i;
            }
        }
        if(last==0){
            res = k;
            for(int i=2;i<=len;i++)res = (res*(k-1))%mod;
        }
        else if(last !=len){
            res = res * (d[len-last-1][0] + (k-1)*d[len-last-1][1]%mod)%mod;
        }
        return res;
    }
    int main(){
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;i++){
            if(i&1)scanf("%d",&a[(i+1)/2]);
            else scanf("%d",&b[i/2]);
        }
        d[0][0] = 0;d[0][1] = 1;
        for(int i=1;i<=(n+1)/2;i++){
            if(i&1){
                int len = i/2;
                d[i][0] = (d[len][0] * d[len][0]%mod + (k-1) * d[len][1]%mod * d[len][1]%mod)%mod;
                d[i][1] = (d[len][0] * d[len][1]%mod * 2%mod + (k-2) * d[len][1]%mod * d[len][1]%mod)%mod;
            }
            else{
                d[i][0] = (d[i-1][1] * (k-1)) % mod;
                d[i][1] = (d[i-1][0] + (k-2) * d[i-1][1]%mod)%mod;
            }
        }
        printf("%lld
    ",(solve(a,(n+1)/2)*solve(b,n-(n+1)/2))%mod);
        return 0;
    }
    

    CH6101 最优贸易(最短路)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    const int M = 500010;
    int val[N],n,m,d[N],f[N],vis[N];
    vector<int> v[N];
    struct edge{
        int x,y,k;
    }e[M];
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&val[i]);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].k);
            v[e[i].x].push_back(e[i].y);
            if(e[i].k == 2)v[e[i].y].push_back(e[i].x);
        }
        memset(d,0x3f,sizeof d);
        d[1] = val[1];
        priority_queue< pair<int,int> > q;
        q.push(make_pair(-val[1],1));
        while(!q.empty()){
            int x = q.top().second;q.pop();
            if(vis[x])continue;
            vis[x] = 1;
            for(int i=0;i<v[x].size();i++){
                int y = v[x][i];
                if(d[y] > min(val[y],d[x])){
                    d[y] = min(val[y],d[x]);
                    q.push(make_pair(-d[y],y));
                }
            }
        }
        for(int i=1;i<=n;i++){
            v[i].clear();vis[i] = 0;
        }
        for(int i=1;i<=m;i++){
            v[e[i].y].push_back(e[i].x);
            if(e[i].k == 2)v[e[i].x].push_back(e[i].y);
        }
        q.push(make_pair(val[n],n));
        f[n] = val[n];
        while(!q.empty()){
            int x = q.top().second;q.pop();
            if(vis[x])continue;
            vis[x] = 1;
            for(int i=0;i<v[x].size();i++){
                int y = v[x][i];
                if(f[y] < max(val[y],f[x])){
                    f[y] = max(val[y],f[x]);
                    q.push(make_pair(f[y],y));
                }
            }
        }
        int res = 0;
        for(int i=1;i<=n;i++){
            res = max(res,f[i]-d[i]);
        }
        cout<<res<<endl;
        return 0;
    }
    

    CF-1141F2 - Same Sum Blocks (Hard) <贪心,奇技淫巧>

    题意:长度为n(n<=1500)的序列,使得分成尽量多的区间,使得每个区间加起来的和都相等。

    分析:用map存起来每个和对应的区间的左右端点map<int, vector<pair<int,int>>> segs; ,然后对于同一和,试着确定可以分出最多多少个互不相交的区间。一个比较容易想到的贪心策略是,右端点递增的依次选择区间,那么我们一开始筛选区间的时候,就按照右端点从左到右的顺序就可以了

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++)
            cin >> a[i];
        map<int, vector<pair<int,int>>> segs;
        //按照右端点从左到右的存取区间
        for (int r = 0; r < n; r++) {
            int sum = 0;                              
            for (int l = r; l >= 0; l--) {
                sum += a[l];
                segs[sum].push_back({l, r});
            }
        }
        int result = 0;
        vector<pair<int,int>> best;
        for (const auto& p: segs) {
            //const 引用,加快速度
            const vector<pair<int,int>>& pp = p.second;
            int cur = 0;
            int r = -1;
            //存取答案
            vector<pair<int,int>> now;
            for (auto seg: pp)
                if (seg.first > r) {
                    cur++;
                    now.push_back(seg);
                    r = seg.second;
                }
            if (cur > result) {
                result = cur;
                best = now;
            }
        }
        cout << result << endl;
        for (auto seg: best)
            cout << seg.first + 1 << " " << seg.second + 1 << endl;
        return 0;
    }
    

    1119 D - Frets On Fire

    思维题,不难啊,为啥就被唬住了。这种难度估计练铜牌题都不算。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    typedef unsigned long long ll;
    ll s[N],n,q,c[N],sum[N];
    int main(){
        scanf("%lld",&n);
        for(int i=0;i<n;i++){
            scanf("%lld",&s[i]);
        }
        sort(s,s+n);
        for(int i=1;i<n;i++)
            c[i] = s[i] - s[i-1];
        sort(c+1,c+n);
        for(int i=1;i<n;i++)sum[i] = sum[i-1] + c[i];
        scanf("%lld",&q);
        while(q--){
            ll l,r;
            scanf("%lld%lld",&l,&r);
            ll len = r-l+1;
            ll res = s[n-1] - s[0];
            ll pos = upper_bound(c+1,c+n,len) - c;
            res -= (sum[n-1] - sum[pos-1]) - (n-pos) * len;
            res +=  r-l + 1;
            printf("%lld
    ",res);
        }
        return 0;
    }
    

    1119 E - Pavel and Triangles

    贪心啊,过不了肯定就是贪错了,优先1+2的匹配方案

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 300010;
    typedef long long ll;
    ll a[N],n;
    int main(){
        scanf("%d",&n);
        long long res = 0;
        for (int i = 0; i < n; ++i)
        {
            scanf("%lld",&a[i]);
        }
        queue<ll> q;
        ll last = 0;
        ll now = 0;
        for(int i=0;i<n;i++){
            ll add = min(a[i]/2,last);
            res += add;
            last -= add;
            a[i] -= add * 2;
            res += a[i]/3;
            last += a[i]%3;
        }
        cout<<res<<endl;
        return 0;
    }
    

    1139 E - Maximize Mex

    从最后建图,进行二分图匹配。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 5050;
    int n,m,p[N],c[N],d,del[N],res[N],k[N];
    vector<int> v[N];
    int match[N],vis[N];
    bool dfs(int x){
        for(int i=0;i<v[x].size();i++){
            int y = v[x][i];
            if(vis[y])continue;
            vis[y] = 1;
            //如果y没有匹配过,或者继续dfs下去可以成功,则当前match[y]=x,而消除vis[y]只是为了下一圈寻找时不必要初始化vis[y]
            if(match[y]==-1 || dfs(match[y])){
                match[y] = x;
                vis[y] = 0;
                return true;
            }
            vis[y] = 0;
        }
        return false;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&p[i]);
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        scanf("%d",&d);
        for(int i=1;i<=d;i++){
            scanf("%d",&k[i]);
            del[k[i]]=1;
        }
        memset(match,-1,sizeof match);
        for(int i=1;i<=n;i++)if(del[i] == 0)v[p[i]].push_back(c[i]);
        int j=0;//Mex肯定是从0增加的
        for(int i=d;i>=1;i--){
            int id = k[i];
            for(;j<=5000;j++){
                if(!dfs(j))break;//如果没有匹配到,则当前的Mex为j
            }
            res[i] = j;
            v[p[id]].push_back(c[id]);//然后把删除的这个加进去
        }
        for(int i=1;i<=d;i++)
            printf("%d
    ",res[i]);
        return 0;
    }
    
    结语:

    最近做了一些水题吧,感觉并没有什么提高,一遇到有思维量的题目就over了。昨天打比赛打的自闭了,也正因为这样,才把拖欠了两周的记录补上,其实平时也在写着,只不过很凌乱,最近这两周正在看第二遍蓝书。暑假前要把规定的内容过完啊。加油

  • 相关阅读:
    数据结构的理解
    等价、偏序和全序
    等价、偏序和全序
    二叉树与树的理解
    SICP 习题 (2.10)解题总结: 区间除法中除于零的问题
    div:给div加滚动栏 div的滚动栏设置
    textarea文本域宽度和高度(width、height)自己主动适应变化处理
    OSX: 逻辑卷管理系统Core Storage(1)
    垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
    string实现
  • 原文地址:https://www.cnblogs.com/1625--H/p/10706010.html
Copyright © 2011-2022 走看看