The 2022 ICPC Asia Nanjing Regional Contest - 2022南京补题记录 ABDEGIJM (8/13)

 https://codeforces.com/gym/104128

A 前缀和 模拟

B DP deque单调队列优化

D 二分,差分,思维

E 虚树,LCA,树形DP,ST表

https://oi-wiki.org/graph/virtual-tree/ 虚树教程

G 贪心,思维

 I  签到

J 图论 搜索 思维 

M 计算几何

In the 2021 contest, problem A, Oops, It’s Yesterday Twice More, also requires the contestants to construct an operation sequence for the game: This time, every cell in the grid stands exactly one kangaroo. You need to construct an operating sequence consisting only of characters ‘U’, ‘D’, ‘L’, and ‘R’. After applying it, you must make sure every kangaroo will gather at the specific cell (a, b). The length of the operating sequence cannot exceed 3(n − 1). As always, the kangaroos will move simultaneously according to the operation you command. Now, in the 2022 contest, the kangaroo problem is back again! We don’t know why problem setters are so obsessed with kangaroos but the problem is as follows: You are given a grid with n rows and m columns. There is a hole in the cell on the ih-th row and the jh-th column. All other cells are empty and there is one kangaroo standing in each cell. Similarly, the kangaroos are controlled by pressing the button U, D, L, R on the keyboard. All kangaroos will move simultaneously according to the button pressed. Specifically, for any kangaroo located in the cell on the i-th row and the j-th column, indicated by (i, j): 1. Button U: it will move to (i − 1, j). 2. Button D: it will move to (i + 1, j). 3. Button L: it will move to (i, j − 1). 4. Button R: it will move to (i, j + 1). If a kangaroo steps onto the hole (that is, i = ih and j = jh) or steps out of the grid, it will be removed from the grid. The problem is that, the exact value of ih and jh is not known. You’re only given an operating sequence consisting only of characters ‘U’, ‘D’, ‘L’, and ‘R’, and an integer k indicating that after applying the operating sequence, there are k kangaroos remaining on the grid.Calculate the number of possible positions of the hole. That is, calculate the number of integer pairs (ih, jh) such that: • 1 ≤ ih ≤ n, 1 ≤ jh ≤ m. • The hole is located at (ih, jh). • After applying the given operating sequence, the number of kangaroos remaining on the grid is exactly k. Input There are multiple test cases. The first line of the input contains an integer T indicating the number of test cases. For each test case: The first line contains three integers n, m and k (1 ≤ n, m ≤ 103 , 0 ≤ k < n × m) indicating the size of the grid and the number of kangaroos remaining on the grid after applying the operating sequence. The second line contains a string s1s2 · · · sl (si ∈ {‘U’, ‘D’, ‘L’, ‘R’}, 1 ≤ l ≤ 106 ) indicating the operating sequence. It’s guaranteed that neither the sum of n × m nor the total length of the operating sequences of each test case will exceed 106 . Output For each test case output one integer indicating the number of possible positions of the hole

整体移动袋鼠,先忽略掉洞,最终我们是剩下了一个小矩形,或者全部不剩下。再考虑洞的轨迹,最终剩下的小矩形还需要扣除洞轨迹带来的影响。洞的轨迹和整体轨迹相反。常规思路是枚举洞的位置,和小矩形的不同相交情况代表了洞的不同位置。而转化为根据不同相交情况来确认洞的位置。在袋鼠不全部移动出原矩阵时,洞轨迹一定和剩余矩阵有相交!这是本题关键。故答案枚举只在能够产生相交的位置进行枚举。即剩余矩阵右下角在洞轨迹矩形每个点的情况。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 7, mv = 1.5e3 + 3;
int c[N][N], o[N][N];
char s[1000007];
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {

        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        scanf("%s", s);
        int l = strlen(s);
        int x = mv, y = mv;
        int cn = n, cm = m;
        int mix = x, mxx = x, miy = y, mxy = y;
        o[x][y] = 1;
        for (int i = 0; i < l; i++)
        {
            if (s[i] == 'U')
            {
                x++;
            }
            else if (s[i] == 'D')
            {
                x--;
            }
            else if (s[i] == 'L')
            {
                y++;
            }
            else if (s[i] == 'R')
            {
                y--;
            }
            if (x >= max(1, mv - n) && x <=min(N - 1, mv + n) && y >= max(1, mv - m) && y < min(N - 1, mv + m))
            {
                o[x][y] = 1;
            }
            mxx = max(mxx, x);
            mix = min(mix, x);
            mxy = max(mxy, y);
            miy = min(miy, y);
        }
        if (mxx - mix >= n || mxy - miy >= m)
        {
            if (k == 0)
            {
                printf("%d\n", n * m);
            }
            else
            {
                printf("%d\n", 0);
            }
        }
        else
        {
            for (int i = max(1, mv - n); i <= min(N - 1, mv +n); i++)
            {
                for (int j = max(1, mv - m); j <= min(N - 1, mv + cm); j++)
                {
                    c[i][j] = c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1] + o[i][j];
                }
            }
            int res = 0;
            n = n - mxx + mix;
            m = m - mxy + miy;

            for (int i = mix; i < mxx + n; i++)
            {
                for (int j = miy; j < mxy + m; j++)
                {
                    if (c[i][j] + c[i - n][j - m] - c[i - n][j] - c[i][j - m] == n * m - k)
                    {
                        res++;
                    }
                }
            }
            printf("%d\n", res);
        }
        for (int i = max(1, mv - cn); i <= min(N - 1, mv + cn); i++)
        {
            for (int j = max(1, mv - cm); j <= min(N - 1, mv + cm); j++)
            {
                c[i][j] = 0;
                o[i][j] = 0;
            }
        }
    }
}

B. Ropeway

Purple Mountain (Zijin Shan) is one of the most famous mountain in Nanjing and its ropeway, the Purple Mountain Ropeway, used to be the longest chairlift in China. Tourists can take the ropeway from the foot of the mountain and go all the way up to the top in less than ten minutes.

The old Purple Mountain Ropeway. Photo by MiNe. Licensed under CC BY 2.0.

In order to build a ropeway, aside from the stations at the foot and the top of the mountain, we also need to build supporting towers along the way. The engineers have built the stations at 00 and (n+1)(n+1) unit of distance from the entrance of the ropeway and have investigated the cost to build a supporting tower at 1,2,⋯,n1,2,⋯,n unit of distance, which are a1,a2,⋯,ana1,a2,⋯,an respectively.

Due to some of the considerations on engineering, supporting towers must be built at some position. Also, to ensure the safety of the ropeway, the distance between neighboring supporting towers or stations must be less than or equal to kk. That is, let b0,b1,b2,⋯,bm,bm+1b0,b1,b2,⋯,bm,bm+1 be the final positions to build two stations and mm supporting towers, where 0≤m≤n0≤m≤n and 0=b0<b1<b2<⋯<bm<bm+1=n+10=b0<b1<b2<⋯<bm<bm+1=n+1, we have bi−bi−1≤kbi−bi−1≤k for all 1≤i≤m+11≤i≤m+1.

In the meantime, to make plans more flexible, the engineers will temporarily change the cost sequence for qq times. The ii-th change can be described as (pi,vi)(pi,vi), which means changing the value of apiapi to vivi temporarily.

For each change, please calculate the minimum total cost to build supporting towers such that all requirements above can be satisfied.

Please note again that all changes are temporary and independent from each other. That is, after calculating the answer for a change, this change will be reverted and the cost sequence will be reverted back to its original state.

 首先有一个很特殊的数据,即k<=3000,又根据q<=3000,完全可以推测这是一个3000^2的算法,即每次我们修改一个点,只会牵扯到周围3000距离的dp值,联想到前后缀DP。

最终答案即为min(pre[i]+bac[i]-a[i])  

而我们注意到,在一段长度为k的区间内[L,R]内,必须要选一个,所以我们答案就变成了

任选一个K长度的区间,每个点强制选择的DP值的最小值。特别的,当这一区间距离终点不足k的时候,dp[n+1]已经被求得,故不影响。

单调队列预处理之后,每当改动一个点pos, [pos-k,pos+k]的区间的dp值都会被改变,pos+k更右侧的虽然也会被影响,但我们只需要在这一区间内统计答案,就获得了最终的答案。

因为pos位置的更改,pos之前的后缀dp被修改,这里我们强制只统计修改前缀的答案,[pos-k,pos-1]这里的前缀dp值不被影响,先加入队列,[pos,pos+k]位置的,由于pos位置的介入,前缀dp值发生改变。由于[pos,pos+k]位置必须有一个要选的,故新的前缀dp值加上旧的后缀DP值就是答案。

特别的,当一个点必须要被选的时候,意味着接下来选的点都要在他基础上继承,继承时不可以横跨(这样就默认该位置不选)也不可以弹出,这样就意味着被淘汰,所以直接清除单调队列,再加入这一必须要选的点即可

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int d[5000000+10];
ll dppre[500000+10],dpbac[500000+10],a[500000+10];
int n,k;
char s[500000+10];
void workpre()
{
    int head=1,tail=0;
    dppre[0]=0;
    tail++;
    d[tail]=0;
    for(int i=1; i<=n+1; i++)
    {
        while(tail>=head&&(!(i-d[head]<=k)))
            head++;
        dppre[i]=dppre[d[head]]+a[i];
        if(s[i]=='1')
        {
            tail=0;
            head=1;
        }
        while(tail>=head&&dppre[i]<=dppre[d[tail]])
            tail--;
        tail++;
        d[tail]=i;
    }
}
void workbac()
{
    int head,tail;
    head=1;
    tail=0;
    dpbac[n+1]=0;

    tail++;
    d[tail]=n+1;
    for(int i=n; i>=0; i--)
    {
        while(head<=tail&&(!(d[head]-i<=k)))
            head++;
        dpbac[i]=dpbac[d[head]]+a[i];

        if(s[i]=='1')
        {
            head=1;
            tail=0;
        }
        while(head<=tail&&dpbac[i]<=dpbac[d[tail]])
            tail--;

        tail++;
        d[tail]=i;
    }
    for(int i=1; i<=n; i++)
    {
        dpbac[i]-=a[i];
    }
}
ll temp[500000+10];
void work(int pos,int val)
{
    int head,tail;
    head=1;
    tail=0;
    int pre=a[pos];
    a[pos]=val;
    ll ans=1e18;
    for(int i=max(0,pos-k); i<pos; i++)
    {
        while(head<=tail&&(!(i-d[head]<=k)))
            head++;

        if(s[i]=='1')
        {
            head=1;
            tail=0;
        }
        while(head<=tail&&dppre[i]<=dppre[d[tail]])
            tail--;
        tail++;
        d[tail]=i;
        temp[i]=dppre[i];
    }
    for(int i=pos; i<=min(n+1,pos+k-1); i++)
    {
        while(head<=tail&&(!(i-d[head]<=k)))
            head++;
        temp[i]=temp[d[head]]+a[i];
        if(s[i]=='1')
        {
            head=1;
            tail=0;
        }
        ans=min(ans,temp[i]+dpbac[i]);
      while(head<=tail&&temp[i]<=temp[d[tail]])
            tail--;
        tail++;
        d[tail]=i;
    }
    a[pos]=pre;
    cout<<ans<<'\n';
}
int main ()
{

    int t;
    cin>>t;

    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
        }
        a[0]=a[n+1]=0;
        scanf("%s",s+1);
        s[0]='0';
        s[n+1]='0';
        workpre();
        workbac();
        int m;
        scanf("%d",&m);

        while(m--)
        {
            int pos,val;
            scanf("%d%d",&pos,&val);
            work(pos,val);
        }
    }

    return 0;
}

D. Chat Program

You're the researcher of the International Chat Program Company (ICPC). Today, you discover the following chat history when reviewing some research data.

SUA (2022/12/04 23:01:25)

I'm out of ideas for competitive programming problems! Please give me a problem about sequences.

BOT (2022/12/04 23:01:27)

Sure. Here is a competitive programming problem about sequences.

Given an integer sequence a1,a2,⋯,ana1,a2,⋯,an of length nn and four other integers kk, mm, cc and dd, your goal is to maximize the kk-th largest element in the sequence.

To achieve the goal, you can perform the following operation at most once: select a continuous sub-array of length mm and add an arithmetic sequence with length mm, initial term cc and common difference dd to the sub-array.

More formally, you can select an integer pp satisfying 1≤p≤n−m+11≤p≤n−m+1 and add (c+di)(c+di) to ap+iap+i for all 0≤i<m0≤i<m.

Calculate the largest possible value of the kk-th largest element in the sequence after at most one operation.

The kk-th largest element in the sequence is the kk-th element in the sorted sequence after sorting all elements from the largest to the smallest. For example, the 33rd largest element in sequence {5,7,1,9}{5,7,1,9} is 55, while the 33rd largest element in sequence {9,7,5,9}{9,7,5,9} is 77.

SUA (2022/12/05 00:15:17)

This problem seems difficult! Please teach me the solution.

BOT (2022/12/05 00:15:30)

Sure. Firstly, we can...

[DATA EXPUNGED]

Unfortunately, parts of the chat history are lost due to a disk failure. You're amazed at how a chat program can create a competitive programming problem. To verify whether the chat program can create valid problems, you decide to try on this problem.

二分最终答案,[0,1e18],难在check函数怎样去写,首先一个值满足第k大必定是大于等于他的数字个数大于等于k,数列本身存在的,我们累加,对于不存在的,我们每一个多考虑把他们变成>=x的,x为二分答案,其对应的等差序列左端点是一个区间,我们对这个区间进行差分+1,代表,在这些位置放置等差数列首项会有1的收入,由于我们必须放置且只能放置一个等差数列,那么就枚举每个位置放置的贡献取最大即可

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll sum[200000+10],a[200000+10],n,k,m,d,c;

bool check(ll mid)
{
    int cnt=0;

    for(int i=0;i<=n;i++)
    {
        sum[i]=0;
    }

    for(int i=1;i<=n;i++)
    {
        if(a[i]>=mid)
        {
            cnt++;
            continue;
        }

        if(d==0)
        {
            if(a[i]+c>=mid)
            {
                sum[max(1ll,i-m+1)]++;
                sum[i+1]--;
            }
            else
            {
                continue;
            }
        }
        else
        {

           if(a[i]+c>=mid)
           {
               //首相
               sum[max(1ll,i-m+1)]++;
               sum[i+1]--;
           }
           else
           {

               ll cnt=(mid-a[i]-c)/d+((mid-a[i]-c)%d!=0);
               //这是第cnt+1项
               cnt++;

               if(cnt>m||cnt>i)
               {
                   //第m项也无法满足
                   continue;
               }
               else
               {
                   int l=i-m+1;
                   int r=i-cnt+1;
                   sum[max(l,1)]++;
                   sum[r+1]--;
               }
           }

        }
    }

    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+sum[i];

       // cout<<sum[i]<<" = ";
        if(sum[i]+cnt>=k)
        {
           // cout<<endl;
            return 1;
        }
    }

    return 0;
}
int main()
{


    cin>>n>>k>>m>>c>>d;

    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    ll l=0,r=1e18;

   check(4);

   //cout<<endl;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(check(mid))
        {
            l=mid+1;
        }
        else
        {

            r=mid-1;
        }
    }

    cout<<r;
    return 0;
}

 

E. Color the Tree

standard output

There is a rooted tree of nn vertices. The vertices are numbered from 11 to nn (both inclusive) and the tree is rooted at 11. All of the nn vertices are white at the beginning and you need to color all the vertices black.

To help you achieve the goal we will provide you with nn types of operations, numbered from 00 to (n−1)(n−1) (both inclusive). Operation ii (0≤i≤n−10≤i≤n−1) requires you to first select a vertex uu, then color all the vertices vv satisfying the following conditions black:

  • Vertex vv is in the subtree rooted at uu, which means u=vu=v or uu is an ancestor of vv.
  • The distance between vertices uu and vv is exactly ii. Here the distance between uu and vv is the minimum number of edges we need to go through to reach vv from uu.

The cost of performing operation ii once is given as aiai. A vertex can be colored more than once and all operations can be performed any number of times. Calculate the minimum total cost to color all the vertices black.

每次染色,层与层之间并不影响,一次只能染色一层,染色一层算一次,故我们枚举每一层染色的花费。我们枚举的点并不会超过N,采用虚树来优化这一过程,对每一层暴力建树,跑树形DP,原始的dp方程为 dp[i]代表将i子树 D深度全部染成黑色的花费,

dp[i]=min(a[D-dep[i]], sum(dp[som[i]])),即本位置可以操作也可以不操作,不操作的时候儿子必须全部操作。特别的,叶子结点的时候,必须操作。

而在虚树上面,x的儿子们和x并非直接相连,本次不操作的时候,每个儿子其实有两种选择,一是本来的dp值,而是 儿子和本节点原路径上花费的最小值,路径点的深度连续,与D的差值连续,区间最值ST表维护 O(1)查询即可。本次操作,与原始dp方程一样。

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

struct node
{
    int b,e;
};
struct node s[1000000+10];
int f[1000000+10],nex[1000000+10],len,n;
int a[1000000+10],dep[1000000+10],dfn[1000000+10],timecnt;
int fa[1000000+10][31];

vector<int>v[1000000+10];
int maxdep;
void swim(int &x,int cha)
{
    for(int i=0; (1<<i)<=cha; i++)
    {
        if((cha)&(1<<i))
        {
            x=fa[x][i];
        }
    }
}
int getlca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int cha=dep[x]-dep[y];
    swim(x,cha);
    if(x==y)
        return x;
    for(int i=30; i>=0; i--)
    {
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i];
            y=fa[y][i];
        }
    }

    return fa[x][0];
}
void dfs(int now,int pre)
{

    dep[now]=dep[pre]+1;
    v[dep[now]].push_back(now);
    maxdep=max(maxdep,dep[now]);
    fa[now][0]=pre;
    timecnt++;
    dfn[now]=timecnt;
    int x=f[now];
    while(x!=-1)
    {
        int j=s[x].e;
        if(j==pre)
        {
            x=nex[x];
            continue;
        }
        dfs(j,now);
        x=nex[x];
    }
}
void add(int x,int y)
{
    s[len].b=x;
    s[len].e=y;
    nex[len]=f[x];
    f[x]=len;
    len++;
}
int stmax[100000+10][31];
int query(int x,int y)
{
    int len(y-x+1);
    int k=log2(len);
    return min(stmax[x][k],stmax[y-(1<<k)+1][k]);
}
int temp[1000000+10],h[1000000+10];
bool cmp(int x,int y)
{
    return dfn[x]<dfn[y];
}
int nowfa[10000000+10];
ll dp[1000000+10];
void work(int now,int pre)
{
    int flag=0;
    dp[now]=0;
    ll sum=0;
    int x=f[now];
    while(x!=-1)
    {
        int j=s[x].e;
        if(j==pre)
        {
            x=nex[x];
            continue;
        }
        work(j,now);
        int sondep=maxdep-dep[j]+1;
        int nowdep=maxdep-dep[now];

        sum+=min(dp[j],(ll)query(sondep,nowdep));
        flag=1;

        x=nex[x];
    }

    dp[now]=sum;
    if(flag==0)
    {
        dp[now]=a[0];
    }
    else
    {
        dp[now]=min(dp[now],(ll)a[maxdep-dep[now]]);
    }
    f[now]=-1;

}
int main ()
{

    memset(f,-1,sizeof(f));

    int t;
    cin>>t;

    while(t--)
    {

        len=0;
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            v[i+1].clear();
            stmax[i][0]=a[i];
        }
        for(int i=1; i<n; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for(int j=1; j<=20; j++)
        {
            for(int i=0; i+(1<<j)-1<n; i++)
            {
                stmax[i][j]=min(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
            }
        }
        maxdep=0;
        dfs(1,0);
        for(int j=1; j<=30; j++)
        {
            for(int i=1; i<=n; i++)
            {
                fa[i][j]=fa[fa[i][j-1]][j-1];
            }
        }
        ll ans=0;
        memset(f,-1,sizeof(f));
        while(maxdep)
        {
            len=0;
            int nowsize=v[maxdep].size();
            for(int i=1; i<=nowsize; i++)
            {
                temp[i]=v[maxdep][i-1];
            }
            nowsize++;
            temp[nowsize]=1;
            sort(temp+1,temp+1+nowsize,cmp);
            int nowlen=0;
            for(int i=1; i<nowsize; i++)
            {
                nowlen++;
                h[nowlen]=temp[i];
                nowlen++;
                h[nowlen]=getlca(temp[i],temp[i+1]);
            }
            nowlen++;
            h[nowlen]=temp[nowsize];
            sort(h+1,h+1+nowlen,cmp);
            nowlen=unique(h+1,h+1+nowlen)-h-1;
            for(int i=1; i<nowlen; i++)
            {
                int lca=getlca(h[i],h[i+1]);
                nowfa[h[i+1]]=lca;
                add(lca,h[i+1]);
            }
            work(1,0);
            ans+=dp[1];
            maxdep--;
        }
        cout<<ans<<'\n';

    }


    return 0;
}

 G. Inscryption

output

standard output

You are lost deep in the forest. The only thing that is still accompanying you is your stoat. It has an initial attack of 11. It is your only Beast at the beginning.

A single path revealed itself before you. On the path are nn event marks. Every event mark falls into one of the following:

  • Card Choice: A dentizen of the forest shall grace your caravan. You will gain an additional Beast. It will always have an initial attack of 11.
  • Mysterious Stone: You will be compelled to make a worthy sacrifice. Two Beasts from your caravan of your choice will perform the ritual: one to be lost forever, adding its attack onto the other. Failure to perform the ritual will forbid you to go on.
  • Fork in the Road: You will choose to trigger either a Card Choice or a Mysterious Stone. You can't choose to do nothing.

When you walk through the winding road, the event marks will be triggered in order. Find out the maximum average of attack for your Beasts you can achieve after all event marks are completed.

每次添加1,会增加个数与总和,每次加-1,不会减少总和,但会减少个数,故我们可以得出两个结论,一是最终答案根据1,-1个数确定,而是贪心的尽量多放-1。能放就放显然是不对的,我们需要加入一个反悔小贪心,记录0转-1的次数,若本次不满足,就吐出来,难处理的是边界问题,细节不少,算是模拟与贪心

#include <bits/stdc++.h>
using namespace std;
int a[1000000 + 10];
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        scanf("%d", &n);

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

        int flag = 0;
        for (int i = 1; i <= n; i++)
        {
            if (a[i] == 1)
            {
                cnt1++;
            }
            else if (a[i] == -1)
            {
                cnt_1++;
                if (cnt1 <= cnt_1 - cnt0)
                {
                    flag = 1;
                    break;
                }
                else if (cnt1 <= cnt_1)
                {
                    int temp = (cnt_1 + 1 - cnt1 + 1) / 2;
                    cnt0 -= temp;
                    cnt_1 -= temp;
                    cnt1 += temp;
                }
            }
            else
            {
                if (cnt1 > cnt_1 + 1)
                {
                    cnt_1++;
                    cnt0++;
                }
                else
                {
                    cnt1++;
                }
            }
        }

        if (flag)
        {
            cout << -1 << '\n';

            continue;
        }
        int sum = cnt1;
        int cnt = cnt1 - cnt_1;
        int gcd = __gcd(sum, cnt);

        cout << sum / gcd << " " << cnt / gcd << '\n';
    }
    return 0;
}

 I. Perfect Palindrome

Given a string S=s0s1⋯sn−1S=s0s1⋯sn−1 of length nn, let f(S,d)f(S,d) be the string obtained by shifting SS to the left dd times. That is f(S,d)=s(d+0)modns(d+1)modn⋯s(d+n−1)modnf(S,d)=s(d+0)modns(d+1)modn⋯s(d+n−1)modn. We say SS is a perfect palindrome if for all non-negative integer dd, f(S,d)f(S,d) is a palindrome.

You're now given a string A=a0a1⋯an−1A=a0a1⋯an−1 of length nn consisting only of lower-cased English letters. You can perform the following operation on AA any number of times (including zero times): Choose an integer ii such that 0≤i<n0≤i<n and change aiai to any lower-cased English letter.

Calculate the minimum number of operations needed to change AA into a perfect palindrome.

签到,没什么说的

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        string s;
        cin >> s;
        map<char, int> m;
        for (auto i : s)
        {
            m[i]++;
        }
        int mx = 0;
        for (auto i : m)
        {
            mx = max(i.second, mx);
        }
        printf("%d\n", s.length() - mx);
    }
}

J. Perfect Matching

standard output

Given an undirected graph with nn vertices (nn is even) and also given nn integers a1,a2,⋯,ana1,a2,⋯,an, for all positive integers ii and jj satisfying 1≤i<j≤n1≤i<j≤n and |i−j|=|ai−aj||i−j|=|ai−aj| (|x||x| indicates the absolute value of xx) we connect vertices ii and jj with an undirected edge in the graph. It's obvious that this undirected graph does not contain self loops or multiple edges.

Find a perfect matching of this undirected graph, or state that a perfect matching does not exist.

Recall that a perfect matching of a graph is a subset of size n2n2 of all the edges in the graph, such that each vertex in the graph is connected by one edge in this subset.

Input

There are multiple test cases. The first line of the input contains an integer TT indicating the number of test cases. For each test case:

The first line contains an even integer nn (2≤n≤1052≤n≤105) indicating the number of vertices in the undirected graph.

The second line contains nn integers a1,a2,⋯,ana1,a2,⋯,an (−109≤ai≤109−109≤ai≤109).

It's guaranteed that the sum of nn of all test cases does not exceed 106106.

 对连边条件进行转化,发现连边时,ai+i =aj+j  或者 ai-i =aj -j

我们把点抽象为边,每个数字ai,化为边,去连接ai+i aj+j 这左右集合。得出同一个点连接的边,是原图中可以连边的点。且这些点两两之间都可以连边,故为完全图。

那么一个抽象图的一个点所连接的全部边,其实就是原图中tarjan缩点之后的点。原图若能够两两匹配,即一个完全图内部能够两两匹配,并把未匹配的分配给相邻完全图,那么最终便是合法的。一个完全图点个数为偶数时,显然本身能够满足,但不代表本完全图全部占用后,相邻完全图是否满足,所以应该写一个类似于匈牙利的搜索。在抽象图中,本节点管辖的边,以及其他点到达本节点的这条边x,都在我们分配范围之内。每一条边,先进行其他集合的访问(抽象图中点的访问),若访问完之后还没有被标记,则说明我们本次是可以用的,仍旧是两两匹配。贪心的想,如果我们先用了到达本节点的这条边x,和本集合的点进行匹配,会很有可能导致本集合出现一个边无法匹配。相反,先匹配本集合的边,能够匹配完全时,x可以返回给他的原集合,不能完全匹配时,也可以调用x进行匹配。后者显然更为优秀。

抽象图中,对抽象点的标记,本质上是对原图中某一完全图的整体标记,代表这一集合已经实现完全匹配。

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

ll L[1000000+10],R[1000000+10],a[1000000+10];
ll lisanl[1000000+10],lisanr[1000000+10];
vector<pair<int,int>>v[1000000+10];
int vis[1000000+10];
vector<pair<int,int>>ans;
int book[1000000+10];
void dfs(int now,int pre)
{
    int lst=0;
     vis[now]=1;
     for(auto it:v[now])
     {
         if(it.second==pre)
            continue;
         if(vis[it.first]==0)
         {
             dfs(it.first,it.second);
         }

         if(book[it.second]==0)
         {
             if(lst)
             {
                 book[lst]=1;
                 ans.push_back(make_pair(lst,it.second));
                 book[it.second]=1;
                 lst=0;
             }
             else
             {
                 lst=it.second;
             }
         }
     }
     if(lst&&pre)
     {
        ans.push_back(make_pair(lst,pre));
        book[pre]=1;
        book[lst]=1;
     }
}
int main ()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        ans.clear();
        int len1=n,len2=n;
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
            lisanl[i]=a[i]-i;
            lisanr[i]=a[i]+i;
            L[i]=a[i]-i;
            R[i]=a[i]+i;
            vis[i]=vis[i+n]=0;
            book[i]=0;
        }

        if(n%2==1)
        {
            cout<<"No"<<'\n';
            continue;
        }
        sort(lisanl+1,lisanl+1+n);
        sort(lisanr+1,lisanr+1+n);
        len1=unique(lisanl+1,lisanl+1+n)-lisanl-1;
        len2=unique(lisanr+1,lisanr+1+n)-lisanr-1;

        for(int i=1; i<=len1+len2; i++)
        {
            v[i].clear();
        }
        for(int i=1; i<=n; i++)
        {
            L[i]=lower_bound(lisanl+1,lisanl+1+len1,L[i])-lisanl;
            R[i]=lower_bound(lisanr+1,lisanr+1+len2,R[i])-lisanr;
            R[i]+=len1;
            v[L[i]].push_back(make_pair(R[i],i));
            v[R[i]].push_back(make_pair(L[i],i));
        }

        for(int i=1; i<=len1+len2; i++)
        {
            if(vis[i]==0)
            {
                dfs(i,0);
            }
        }

        if(ans.size()==n/2)
        {
            cout<<"Yes"<<'\n';

            for(auto it:ans)
            {
                cout<<it.first<<" "<<it.second<<'\n';
            }
        }
        else
        {
            cout<<"No"<<'\n';
        }

    }

    return 0;
}

M. Drain the Water Tank

The water plant has made a new water tank with a polygon shape and negligible thickness recently.

To bring the water tank into service, the engineers are going to install some drain valves on the tank. A drain valve is considered a point on the water tank, and the water will flow out of the tank through the drain valve when it is on.

As illustrated above, valves are represented by purple dots and the light blue areas are the water remaining in the tank after all valves are turned on.

You, as the chief engineer, are willing to know the minimum number of drain valves needed so that all the water in the tank can be drained off when turning on all the drain valves simultaneously.

You may assume that the water is an ideal fluid and no atmospheric pressure here, so the water always tends to flow to somewhere strictly lower, even when the water is on a horizontal stage.

计算几何一窍不通,附队友代码 

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 7;
int x[N], y[N];
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &x[i], &y[i]);
    }
    bool a = 0;
    int res = 0;
    if (y[n] > y[1])
    {
        a = 1;
        // puts("ok\n");
    }
    else if (y[n] == y[1])
    {
        for (int i = n; i >= 1; i--)
        {
            if (y[i] > y[i - 1])
            {
                a = 0;
                break;
            }
            else if (y[i] < y[i - 1])
            {
                a = 1;
                break;
            }
        }
    }
    x[0] = x[n], y[0] = y[n];
    for (int i = 1; i < n; i++)
    {
        if (a)
        {
            if (y[i] < y[i + 1])
            {
                if ((x[i + 1] - x[i]) * (y[i] - y[i - 1]) - (y[i + 1] - y[i]) * (x[i] - x[i - 1]) < 0)
                {
                    res++;
                    // printf("%d\n", i);
                }
                a = 0;
            }
        }
        else
        {
            if (y[i] > y[i + 1])
            {
                a = 1;
                // printf("%d\n", i);
            }
        }
    }
    if (a)
    {
        if (y[n] < y[1] && ((x[1] - x[n]) * (y[n] - y[n - 1]) - (y[1] - y[n]) * (x[n] - x[n - 1]) < 0))
        {
            res++;
        }
    }
    // printf("%d\n", a);
    printf("%d\n", res);
}
//

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秦三码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值