2019年浙江省第十六届省赛部分题解--The 16th Zhejiang Provincial Collegiate Programming Contest

F、Abbreviation

WA+3(。。。

第一发:CE,交成了C!!!

第二发:scanf("%d",&t);while(t--)写成了while(~scanf("%d",&t))!!!

第三发:输出换行错误!!!

E、Sequence in the Pocket

题意:

DreamGrid has just found an integer sequence  in his right pocket. As DreamGrid is bored, he decides to play with the sequence. He can perform the following operation any number of times (including zero time): select an element and move it to the beginning of the sequence.

What's the minimum number of operations needed to make the sequence non-decreasing?

Input

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

The first line contains an integer  (), indicating the length of the sequence.

The second line contains  integers  (), indicating the given sequence.

It's guaranteed that the sum of  of all test cases will not exceed .

Output

For each test case output one line containing one integer, indicating the answer.

长度为n的数组a,每次移动可以任选一个数放到最前面,求最少移动多少次,使得数列单调不减。

思路:贪心,若值x动了,那么小于x的值一定都要按序提到前面去,所以一定是从最大值开始考虑,尽量不动,一个数只有在所有比它大的数的左面,这个数就可以不动,所以权值相同时,还要按下标排序。不过当遇到某数需要前移时,还需考虑,左面,权值相同,并且在所有比它大的数的左面的数不用移动,

详见代码,例如:1 1 2 2 2 3 4 2 5 2,可以跟着代码理解 

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long int
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=100005;
int n,ans;
struct AA
{
    int rt,x;
    bool operator<(const AA&aa)const
    {
        if(x==aa.x) return rt<aa.rt;
        return x<aa.x;
    }
}pos[maxn];
int main()
{
   int t;
   scanf("%d",&t);
   while(t--)
   {
       ans=0;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
       {
           scanf("%d",&pos[i].x);
           pos[i].rt=i;
       }
       sort(pos+1,pos+1+n);
       int l=0,r=1;
       for(int i=n-1;i>=1;i--)///从权值大的开始考虑
       {
           if(pos[i].rt>pos[i+1].rt) {///i点一定需要移动
                for(int j=i-1;j>=1;j--)
                {
                    if(pos[j].x!=pos[i].x)///权值相同的,最左面的在sort后的相对位置
                    {
                        r=j+1;break;
                    }
                    if(l==0&&pos[j].rt<pos[i+1].rt)///最右面一个不用移动的数
                    {
                        l=j;
                    }
                }
                ans=i;
                if(l!=0)
                {
                    ans-=l-r+1;
                }
                break;
           }
       }
       printf("%d\n",ans);
   }
   return 0;
}

 

 

 

 

 

 

J、Welcome Party

题意:

The 44th World Finals of the International Collegiate Programming Contest (ICPC 2020) will be held in Moscow, Russia. To celebrate this annual event for the best competitive programmers around the world, it is decided to host a welcome party for all  participants of the World Finals, numbered from  to  for convenience.

The party will be held in a large hall. For security reasons, all participants must present their badge to the staff and pass a security check in order to be admitted into the hall. Due to the lack of equipment to perform the security check, it is decided to open only one entrance to the hall, and therefore only one person can enter the hall at a time.

Some participants are friends with each other. There are  pairs of mutual friendship relations. Needless to say, parties are more fun with friends. When a participant enters the hall, if he or she finds that none of his or her friends is in the hall, then that participant will be unhappy, even if his or her friends will be in the hall later. So, one big problem for the organizer is the order according to which participants enter the hall, as this will determine the number of unhappy participants. You are asked to find an order that minimizes the number of unhappy participants. Because participants with smaller numbers are more important (for example the ICPC director may get the number 1), if there are multiple such orders, you need to find the lexicographically smallest one, so that important participants enter the hall first.

Please note that if participant  and  are friends, and if participant  and  are friends, it's NOT necessary that participant  and  are friends.

n个人,m组朋友关系,朋友关系不传递。安排n个人的进屋顺序,若进屋时,屋内没有其朋友,就会不高兴,求不高兴的人数最小的情况下,进入屋子的最小字典序顺序

思路:并查集+优先队列,朋友关系是双向的,不开心的人的数量就是关系图的块数,每个块,需且只需一个人不开心,剩下的人都可以通过关系一个个进屋,不至于不开心,所以并查集判断关系分块,选定不开心的人为下标最小的,让其成为块内最早进屋的,由此决定字典序最小。

然后就用优先队列,每次选择可以进屋的人中编号最小的让其进屋,

首先将所有不高兴的人进队列,选出最小的编号的人,让其先进屋,然后将其朋友,加入队列(后面都是没有加入队列的加入过队列即可),在选最小的,

例:6 4

1 3 

3 5

2 4

4 6

跟着代码看,比较好理解

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long int
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=1000005;
int pre[maxn],ans,t[maxn];
int find(int x)
{
   int r=x;
   if(x==pre[x]) return x;
   else
   return pre[x]=find(pre[x]);
}
void join(int x,int y)
{
   int fx=find(x);
   int fy=find(y);
   if(fx!=fy)
   {
       ans--;//cout<<x<<" ! "<<y<<endl;
        if(fx<fy)
        pre[fy]=fx;
        else
        {
            pre[fx]=fy;
        }
   }
   return;
}
struct AA
{
    int v,next;
}pos[maxn*3];
int n,m,a,b,vis[maxn],f[maxn],num,k;
void add(int x,int y)
{
    pos[++num].v=y;
    pos[num].next=f[x];
    f[x]=num;

    pos[++num].v=x;
    pos[num].next=f[y];
    f[y]=num;
}
priority_queue<int,vector<int>, greater<int> >pq;
void dfs()
{
    while(!pq.empty())
    {
        int x=pq.top();
        k++;
        if(k==n)
            printf("%d\n",x);
        else printf("%d ",x);
        pq.pop();
        for(int i=f[x];i!=-1;i=pos[i].next)
        {
            int v=pos[i].v;
            if(vis[v]) continue;
            else {vis[v]=1;pq.push(v);}
        }
    }
    return;
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        while(!pq.empty()) pq.pop();
        scanf("%d%d",&n,&m);
        k=0;
        ans=n;
        for(int i=1;i<=n;i++)
        {
            pre[i]=i;
            vis[i]=0;
            f[i]=-1;
        }
        num=0;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
            join(a,b);
        }
        printf("%d\n",ans);
        for(int i=1;i<=n;i++)
        {
            if(vis[find(i)]==0)
            {
                vis[find(i)]=1;
                pq.push(find(i));
            }
            else continue;
        }
        dfs();
    }
}

 

B、Element Swapping

题意:

DreamGrid has an integer sequence  and he likes it very much. Unfortunately, his naughty roommate BaoBao swapped two elements  and  () in the sequence when DreamGrid wasn't at home. When DreamGrid comes back, he finds with dismay that his precious sequence has been changed into !

What's worse is that DreamGrid cannot remember his precious sequence. What he only remembers are the two values

Given the sequence after swapping and the two values DreamGrid remembers, please help DreamGrid count the number of possible element pairs BaoBao swaps.

Note that as DreamGrid is poor at memorizing numbers, the value of  or  might not match the sequence, and no possible element pair can be found in this situation.

Two element pairs  () and  () are considered different if  or .

思路:若swap(a[i],a[j]),i*a[i]->i*a[j],j*a[j]->j*a[i],所以原x变化为x'=x+(j-i)(a[i]-a[j]),原y变化为y'=y+(j-i)(a[i]^{2}-a[j]^{2})

\frac{x'-x}{y'-y}=a[i]+a[j],然后就各种分析判断,然后就排个序,O(n)的扫,找i,j即可,队友写的,上代码看。

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=100010;
int n,m,k;
ll sum[maxn],x,y,X,Y;
ll ans,ct,cnt,tmp,flag;
struct node
{
    ll v,id;
    bool operator<(node aa)const
    {
        return v<aa.v;
    }
}a[maxn];
int ok[maxn];
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--){
        scanf("%d%lld%lld",&n,&X,&Y);
        x=X;y=Y;
        rep(i,1,n)
        {
            ok[i]=0;
            scanf("%lld",&a[i].v);
            x-=(ll)i*a[i].v;
            y-=(ll)i*a[i].v*a[i].v;
            a[i].id=i;
        }
        sort(a+1,a+n+1);
        ans=0;
        if(x==0&&y) {puts("0");continue;}
        if(x==0&&y==0)
        {
            int i=1;
            ll tmp=0;
            while(i<=n)
            {
                tmp=0;
                int j=i;
                while(j<=n&&a[j].v==a[i].v) {j++;tmp++;}
                ans+=(tmp*(tmp-1))/2;
                i=j;
            }
            printf("%lld\n",ans);
            continue;
        }
        else if(y%x) {puts("0");continue;}
        else {
            int tim=1;
            y=y/x;
            int i=1,j=n;
            while(i<j)
            {
                tim++;
                int k=i;
                ll tmp=0;
                ll cnt=0;
                while(k<j&&2LL*a[k].v==y&&a[k].v==a[i].v)
                k++;
                if(i!=k) {i=k;continue;}
                tmp=0;
                while(j>i&&a[j].v+a[i].v>y) j--;
                if(j==i) break;
                if(a[i].v+a[j].v!=y) {i++;continue;}
                if(x%(a[j].v-a[i].v)!=0) {i++;continue;}
                ll z=x/(a[j].v-a[i].v);
                //cout<<x<<" "<<a[j].v-a[i].v<<" "<<z<<endl;
                k=i;
                while(k<j&&a[k].v==a[i].v) {
                    if((ll)a[k].id-z<=(ll)n&&(ll)a[k].id-z>0LL)
                    {
                        ok[a[k].id-(int)z]=tim;
                        //cout<<a[k].id<<" "<<z<<endl;
                    }
                    k++;
                }
                while(j>=k&&a[i].v+a[j].v==y) {
                    if(ok[a[j].id]==tim) ans++;
                    j--;
                }
                i=k;
            }
        }
        printf("%lld\n",ans);
      //  if(flag) puts("Yes"); else puts("No");
    }
    return 0;
}

K、Strings in the Pocket

BaoBao has just found two strings  and  in his left pocket, where  indicates the -th character in string , and  indicates the -th character in string .

As BaoBao is bored, he decides to select a substring of  and reverse it. Formally speaking, he can select two integers  and  such that  and change the string to .

In how many ways can BaoBao change  to  using the above operation exactly once? Let  be an operation which reverses the substring , and  be an operation which reverses the substring . These two operations are considered different, if  or .

Input

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

The first line contains a string  (), while the second line contains another string  (). Both strings are composed of lower-cased English letters.

It's guaranteed that the sum of  of all test cases will not exceed .

马拉车+暴力

思路:s和t相同时,就是找s的回文子串的数量,

s,t不相同时,左边和右边连续的相同的可以参与反转,也可以不参与反转,中间最小的、一定要参与反转的部分(就是左边第一个不相同的点到右边第一个s、t不相同的点的长度)然后判断反转后是否s、t能够一样,若不能,答案为0,否则,就看两边加上,反转不变,

代码:

#include <bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
#define ll long long int
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=2000005;
char s[maxn],t[maxn];
int Len[maxn<<1],pos;
char tmp[maxn<<1];
int l,r;
ll ans;
int init(char *st)
{
    int i,len=strlen(st);
    tmp[0]='@';
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';
    tmp[2*len+3]=0;
    return 2*len+1;
}
void manacher(char *st,int len)
{
    int mx=0,po=0;
    for(int i=1;i<=len;i++)
    {
        if(mx>i)
        Len[i]=min(mx-i,Len[2*po-i]);
        else
        Len[i]=1;
        while(st[i-Len[i]]==st[i+Len[i]])
        Len[i]++;
        if(Len[i]+i>mx)
        {
            mx=Len[i]+i;
            po=i;
        }
        l=(i-1)/2-(Len[i]-1)/2;
        r=(i-1)/2+(Len[i]-1)/2;
        if(Len[i]&1) r--;
        //cout<<l<<" "<<r<<endl;
        ans+=((r-l+2)/2);
    }
    printf("%lld\n",ans);
    return ;
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        ans=0;
        scanf("%s%s",s,t);
        int n=strlen(s);
        l=-1;
        for(int i=0;i<n;i++)
        {
            if(s[i]!=t[i]) {l=i;break;}
        }
        if(l!=-1)
        {
            r=-1;
            for(int i=n-1;i>=0;i--)
            {
                if(s[i]!=t[i]) {r=i;break;}
            }
            int j=r,ok=0;
            for(int i=l;i<=r;i++,j--)
            {
                if(s[i]!=t[j])
                {
                    ok=1;break;
                }
            }
            if(ok)
            {
                printf("0\n");
            }
            else
            {
                ans=1;
                for(int i=1;i<=l&&r+i<n;i++)
                {
                    if(s[l-i]==s[r+i]) ans++;
                    else break;
                }
                printf("%lld\n",ans);
            }
        }
        else///s==t的时候
        {
            int len=init(s);
            manacher(tmp,len);
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值