The 2021 ICPC Asia Nanjing Regional Contest (XXII Open Cup, Grand Prix of Nanjing) -2021 南京补题(7/13)

A 签到

C 思维或者DP

D 打表 数据结构 树状数组

H 树形DP

I  DP,几何

J 记忆化搜索

M 思维

E 线段树维护矩阵,待补

 Oops, It's Yesterday Twice More

初始的时候全部格子都有袋鼠,让最终全部袋鼠都落在一个格子里面,移动步数不能超过3*(n-1)。考虑全部袋鼠先移动到同一个距离a,b最近的角落,花费不会超过2*(n-1)。每个最近的角落到袋鼠最多只有(n-1)次移动

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{

    int n, a, b;
    cin >> n >> a >> b;
    if (a * 2 <= n && b * 2 <= n)
    {
        for (int i = 1; i < n; i++)
            cout << 'L';
        for (int i = 1; i < n; i++)
            cout << "U";
        for (int i = 1; i < a; i++)
            cout << "D";
        for (int i = 1; i < b; i++)
            cout << "R";
    }
    else if (a * 2 > n && b * 2 <= n)
    {
        for (int i = 1; i < n; i++)
            cout << 'L';
        for (int i = 1; i < n; i++)
            cout << "D";
        for (int i = n; i > a; i--)
            cout << "U";
        for (int i = 1; i < b; i++)
            cout << "R";
    }
    else if (a * 2 <= n && b * 2 > n)
    {
        for (int i = 1; i < n; i++)
            cout << 'U';
        for (int i = 1; i < n; i++)
            cout << "R";
        for (int i = 1; i < a; i++)
            cout << "D";
        for (int i = n; i > b; i--)
            cout << "L";
    }
    else
    {
        for (int i = 1; i < n; i++)
            cout << 'D';
        for (int i = 1; i < n; i++)
            cout << "R";
        for (int i = n; i > a; i--)
            cout << "U";
        for (int i = n; i > b; i--)
            cout << "L";
    }

    return 0;
}

Klee in Solitary Confinement

赛时用的dp, 因为这种只进行一次区间操作的模型是很经典的。由于答案最多只涉及两种数字,一种是最终确定答案数量的x,一种是能够变成x的x-k,对这两种数字做dp,  dp[0]代表当前没进行+k操作, dp[1]代表当前正在进行+k操作,dp[2]代表+k操作已经进行完。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 7;
int pos[4000000+10];
int dp[1000000 + 10][2][3], a[1000000 + 10];
int cnt[4000000+10];
int main()
{

    int n;
    cin >> n;
    int k;
    cin >> k;
    int fuck=2000000;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        a[i]+=fuck;
    }
    if(k==0)
    {
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            cnt[a[i]]++;
            ans=max(ans,cnt[a[i]]);
        }
        cout<<ans;
        return 0;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        int pre = a[i] - k;
        int prepos = max(pos[pre], pos[a[i]]);
        int flag=1;
        if(prepos==pos[pre])
            flag=0;
        dp[i][1][0] = dp[prepos][flag][0] + 1;
        dp[i][1][1] = max(dp[prepos][flag][0], dp[prepos][flag][1]);
        dp[i][1][2] = max(dp[prepos][flag][1], dp[prepos][flag][2]) + 1;
        ans = max(ans, dp[i][1][0]);
        ans = max(ans, dp[i][1][1]);
        ans = max(ans, dp[i][1][2]);
        pre = a[i] + k;
        prepos = max(pos[pre], pos[a[i]]);
        flag=0;
        if(prepos==pos[pre])
            flag=1;
        dp[i][0][0] = dp[prepos][flag][0];
        dp[i][0][1] = max(dp[prepos][flag][0], dp[prepos][flag][1]) + 1;
        dp[i][0][2] = max(dp[prepos][flag][1], dp[prepos][flag][2]);
        pos[a[i]] = i;
        ans = max(ans, dp[i][0][0]);
        ans = max(ans, dp[i][0][1]);
        ans = max(ans, dp[i][0][2]);

    }

    cout << ans;
}

 Paimon Sorting

打表题。还是很难发现规律的,特别是第三点。考虑当前ans[i]由ans[i-1]递推。 先打一个正常的排列观察答案。再在末尾加不同的一个数字,发现加的数字x=max(a[1]...a[i-1])的时候,ans[i]=ans[i-1], <max(a[1],.....a[i-1])的时候,ans[i]=ans[i-1]+cnt(比x大的数字去重后个数) , >max(a[1]....a[i-1])的时候,是ans[i]=ans[i-1]+2+(i-1-pos2+1)  pos2指前面最大值第二次出现的位置与i-1的位置。

这一过程可以用树状数组实现

#include<bits/stdc++.h>

using namespace std;
typedef long long int ll;

int lowbit(int x)
{
    return x&-x;
}
int book[1000000+10];
int sum[1000000+10],n,a[100000+10];
void change(int x,int val)
{
    while(x<=n)
    {
        sum[x]+=val;
        x+=lowbit(x);
    }
}
int getans(int pos)
{
    int ans=0;
    while(pos)
    {
        ans+=sum[pos];
        pos-=lowbit(pos);
    }
    return ans;
}
ll ans[100000+10];
int main()
{

/*
当a[i]<max(a[1],a....a[i-1])的时候,ans[i]=ans[i-1]+比a[i]大的数字个数(去重)

当a[i]=max(a[1],,,.a[i-1])的时候,ans[i]=ans[i-1]

当a[i]>max(a[i].....a[i-1])的时候,ans[i]=ans[i-1]+2+ dis(pos2,i-1)
*/


int t;
cin>>t;

while(t--)
{
    scanf("%d",&n);

    for(int i=1;i<=n;i++)
    {
       scanf("%d",&a[i]);
    }
    int maxx=0,pos2=0;
    for(int i=1;i<=n;i++)
    {

        if(a[i]==maxx)
        {
            ans[i]=ans[i-1];
        }
        else if(a[i]<maxx)
        {
            ans[i]=ans[i-1]+getans(n)-getans(a[i]);

        }
        else
        {
            ans[i]=ans[i-1]+2;
            if(pos2)
            {
                ans[i]+=(i-1-pos2+1);
            }
        }

        if(i==1)
            ans[i]=0;

        if(maxx<a[i])
        {
            maxx=a[i];
            pos2=0;
        }
        else if(maxx==a[i])
        {
            if(pos2==0)
                pos2=i;
        }

        if(book[a[i]]==0)
        {
            book[a[i]]=1;
            change(a[i],1);
        }


    }

     for(int i=1;i<=n;i++)
    {
        if(i!=n)
        cout<<ans[i]<<" ";
        else
        cout<<ans[i];
    }

    cout<<'\n';
    ans[0]=0;
    for(int i=1;i<=n;i++)
    {
        if(book[a[i]])
        change(a[i],-1);
        ans[i]=0;
        book[a[i]]=0;
    }


}

    return 0;
}


 Crystalfly

当一个点选择当前儿子的时候,发现最多只能选择两个儿子,且其中一个必须是t=3的。

选择两个儿子时,第一个走到的儿子节点x,x的全部儿子一定会在走当前另一个儿子的时候跑光。因此设置一个dp0[i]代表i全部儿子跑光的价值,dp0[i]只计算全部儿子dp1值的和,不算儿子val的和。 对于当前节点的另外一个儿子算dp1值与val值,剩余的只算dp1值。显然不能暴力枚举选择情况,考虑总体先加上全部儿子的dp值,记录前二大的,dp0+val-dp1的节点

枚举t=3的节点,当该节点是最大的dp0+val-dp1时,选择次大的,否则选择最大的

选择一个儿子时,就是一个dp1+val最大值加上剩余儿子的dp1值

不选儿子时,就是儿子dp的累加即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void write(ll x)
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int head[N], n, to[N << 1], nxt[N << 1], cnt;
int ti[N];
ll f[N], f1[N], val[N];
void init()
{
    cnt = 0;
    for (int i = 0; i <= n; i++)
    {
        head[i] = 0;
        f[i] = f1[i] = 0;
    }
}
void add(int u, int v)
{
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
}

void dfs(int u, int fa)
{
    ll mxval = 0, sumval = 0;
    pair<int, ll> a[2];
    a[0].first = 0, a[0].second = 1e18, a[1].first = 0, a[1].second = 1e18;
    int ssum = 0;
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (v == fa)
            continue;
        ssum++;
        dfs(v, u);
        ll c = f[v] - (f1[v] + val[v]);
        pair<int, ll> now;
        now.first = v, now.second = c;
        for (int j = 0; j < 2; j++)
        {
            if (now.second < a[j].second)
            {
                pair<int, ll> it;
                it = now;
                now = a[j];
                a[j] = it;
            }
        }
        mxval = max(mxval, val[v]);
        sumval += f[v];
        f[u] += f[v];
        f1[u] += max(f[v], f1[v]);
    }
    f[u] += mxval;
    if (ssum > 1)
        for (int i = head[u]; i; i = nxt[i])
        {
            int v = to[i];
            if (v == fa)
                continue;
            int idx = 0;
            if (v == a[0].first)
                idx++;

            if (ti[v] == 3)
            {
                f[u] = max(f[u], val[v] + sumval - f[a[idx].first] + f1[a[idx].first] + val[a[idx].first]);
            }
        }
}
int main()
{
    int t = read();
    while (t--)
    {
        n = read();
        init();
        for (int i = 1; i <= n; i++)
            val[i] = read();
        for (int i = 1; i <= n; i++)
            ti[i] = read();
        for (int u, v, i = 1; i < n; i++)
        {
            u = read(), v = read();
            add(u, v), add(v, u);
        }
        dfs(1, 0);
        write(f[1] + val[1]);
        puts("");
    }
    return 0;
}

 Cloud Retainer's Game

倒着dp,终点设为dp[0],  在同一条下行对角线的点满足(x+y)%2h=k,在同一条上行对角线的点满足(x-y)%2h=k, 至于为什么是2h而非h,因为极端情况从原点出发到顶部结束再下行到x轴(x+y)=2h,如果用h的话,会有点的重复。

按照x坐标从大到小排序,当前为点的时候,因为点不能反射,所以直接在其所在的上行与下行对角线++即可

如果是镜子,就对两个方向取max

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6+10;

map<int,int>mp;
struct node
{
    int flag,x,y;
};
struct node s[5000000+10];
bool cmp(struct node a, struct node b)
{
    return a.x>b.x;
}
int main()
{
    int t;
    cin>>t;

    while(t--)
    {
        int n,h;
        cin>>h;
        cin>>n;

        mp.clear();
        for(int i=1; i<=n; i++)
        {
           scanf("%d%d",&s[i].x,&s[i].y);
            s[i].flag=1;
        }
        int m;
        cin>>m;
        for(int i=n+1; i<=n+m; i++)
        {
            s[i].flag=0;
             scanf("%d%d",&s[i].x,&s[i].y);
        }

        sort(s+1,s+1+n+m,cmp);

        for(int i=1; i<=m+n; i++)
        {
            if(s[i].flag==0)
            {
                int nowshang=(s[i].x-s[i].y)%(2*h);
                int nowxia=(s[i].x+s[i].y)%(2*h);

                mp[nowshang]++;
                mp[nowxia]++;
            }
            else
            {
                int nowshang=(s[i].x-s[i].y)%(2*h);
                int nowxia=(s[i].x+s[i].y)%(2*h);
                mp[nowshang]=mp[nowxia]=max(mp[nowshang],mp[nowxia]);

            }
        }

        cout<<mp[0]<<'\n';

    }


    return 0;
}

Xingqiu's Joke

 a%p=0,b%p=0 则(a-b)%p=0,反之亦然

又因为+1,-1差值不变,所以a,b能够整除的p始终就是那几十个数字

直接暴力记忆化,这里hash一下 (a,b)的状态,把二维map变成一维的

在a,b变化的时候,枚举每个质数,获取余数mod, 考虑向下或者向上进入这一两种状态。

但这样会t,这时候贪心的想,直接在进入两种状态之前就除,而不是再继续扩展。

#include<bits/stdc++.h>

using namespace std;
typedef long long int ll;

ll gethash(ll x,ll y)
{
    return x*1e9+y;
}
unordered_map<ll,int>mp;
ll m[1000],len;

ll dfs(ll a,ll b)
{

    if(a==1||b==1)
    return 0;

    ll nowhash=gethash(a,b);
    if(mp[nowhash])
        return mp[nowhash];

    ll nowminn=min(a-1,b-1);
    for(int i=1;i<=len;i++)
    {
        ll nowm=a%m[i];

        ll tempa=a-nowm;
        ll tempb=b-nowm;

        if(tempa%m[i]==0&&tempb%m[i]==0)
        nowminn=min(nowminn,nowm+dfs(tempa/m[i],tempb/m[i])+1);

        if((a+m[i]-nowm)%m[i]==0&&(b+m[i]-nowm)%m[i]==0)
        {
            nowminn=min(nowminn,m[i]-nowm+1+dfs((a+m[i]-nowm)/m[i],(b+m[i]-nowm)/m[i]));
        }

    }
    return mp[gethash(a,b)]=nowminn;
}
int main()
{
/*
ab同时除以p
a%p=0
b%p=0

(a-b)%p=0

ab无论怎么变化,其差值不变,能除以的数字是固定的

*/

int t;
cin>>t;

while(t--)
{
   // mp.clear();
    ll a,b;
    cin>>a>>b;
    ll cha=abs(a-b);
    len=0;
    for(ll i=2;i*i<=cha;i++)
    {
        if(cha%i==0)
        {
            len++;
            m[len]=i;
            while(cha%i==0)
                cha/=i;
        }
    }

    if(cha>1)
    {
        len++;
        m[len]=cha;
    }

    cout<<dfs(a,b)<<'\n';
}
    return 0;
}

 

Windblume Festival

有正有负的时候,留一个负数和与之相连的正数,用负数减去全部的正数,在此之前,正数可以吸取合并相邻负数获得更大正数。然后我们用最后一个正数去减去这个减了全部数字的负数,最终就是全部绝对值

只有正数的时候,考虑一个正数减去剩下全部正数,最后剩下两个数,用正减去负

只有负数的时候,考虑一个负数减去剩下全部负数,只剩一个正数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 7;
ll q[N];
ll c[N];
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int n;
        scanf("%d", &n);
        ll s = 0;
        int flag=0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld", &q[i]);
            s += abs(q[i]);

            if(q[i]>0)
                flag=1;
        }
        for (int i = 1; i <= 4; i++)
        {
            q[n + i] = q[i];
        }
        if (n == 1)
        {
            printf("%lld\n", q[1]);
        }
        else if (n == 2)
        {
            printf("%lld\n", max(q[1] - q[2], q[2] - q[1]));
        }
        else if (n == 3)
        {
            ll mx = -1e18;
            for (int i = 1; i <= 3; i++)
            {
                mx = max(mx, q[i] - q[i + 1] + q[i + 2]);
                mx = max(mx, q[i] - q[i + 1] - q[i + 2]);
            }
            printf("%lld\n", mx);
        }
        else
        {
            ll mx;

            if(flag)
            {


                mx = -1e18;
                for (int i = 1; i <= n; i++)
                {
                    mx = max(mx, s - abs(q[i]) - abs(q[i + 1]) + q[i] - q[i + 1]);
                }
            }
            else
            {

                mx=-1e18;

                for(int i=1; i<=n; i++)
                {
                    mx=max(mx,s-abs(q[i])+q[i]);
                }
            }

            printf("%lld\n", mx);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦三码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值