暑假集训日记——8.12(codeforce)

本文精选了五道算法竞赛题目并提供了详细的题解,包括字符串压缩、区域覆盖、数的分解、环形舞蹈和括号序列等,涵盖了KMP算法、滑动窗口、模拟、图论和动态规划等多种算法技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Compress Words
题意:
A m u g a e Amugae Amugae有一个由n个单词组成的句子。他想把这个句子压缩成一个词。 A m u g a e Amugae Amugae不喜欢重复,所以当他将两个单词合并成一个单词时,他删除第二个单词中与第一个单词后缀重合的最长前缀。例如,他将 s a m p l e sample sample p l e a s e please please合并成 s a m p l e a s e samplease samplease
A m u g a e Amugae Amugae将从左到右合并他的句子(即首先合并前两个单词,然后合并结果与第三个单词,以此类推)。编写一个程序,在合并过程结束后打印压缩后的单词。

题解:求 a a a的后缀与 b b b的前缀重合的最大长度 ( e x k m p exkmp exkmp模板题)
光顾着套板子,自己都不动脑子了,由于 e x k m p exkmp exkmp e x [ i ] ex[i] ex[i]表示 s s s i i i开头的后缀与 t t t 的前缀相同的长度, i i i的取值范围为 [ 1 , l e n ] [1,len] [1,len],直接套肯定超时,毕竟求了很多没有用的东西。所以只需要考虑 [ l e n − l 2 , l e n ] [len-l2,len] [lenl2,len]区间内的公共前缀就好了,在板子的基础上改一点。

#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
#define next nx
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;

const int N=1e6+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,cnt;

string s[N];
char ans[N],tmp[N];
int next[N],ex[N]; //ex数组即为extend数组

//预处理计算next数组
void GETNEXT(char *str,int len)
{
    int i=0,j,po;
    next[0]=len;//初始化next[0]
    while(str[i]==str[i+1]&&i+1<len)//计算next[1]
    i++;
    next[1]=i;
    po=1;//初始化po的位置
    for(i=2;i<len;i++)
    {
        if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值
        next[i]=next[i-po];
        else//第二种情况,要继续匹配才能得到next[i]的值
        {
            j=next[po]+po-i;
            if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
            while(i+j<len&&str[j]==str[j+i])//计算next[i]
            j++;
            next[i]=j;
            po=i;//更新po的位置
        }
    }
}
//计算extend数组
void EXKMP(char *s1,char *s2,int len,int l2)
{
    /*************修改ex数组的起始点*****************/
    int i=max(len-l2,0),j=0,po;
    GETNEXT(s2,l2);//计算子串的next数组
    while(s1[i]==s2[j]&&j<l2&&i<len)//计算ex[0]
    i++,j++;
    ex[max(len-l2,0)]=j;
    po=max(len-l2,0);//初始化po的位置
    /*******************************************/
    for(i=max(len-l2,0)+1;i<len;i++)
    {
        if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
        ex[i]=next[i-po];
        else//第二种情况,要继续匹配才能得到ex[i]的值
        {
            j=ex[po]+po-i;
            if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
            while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
            j++;
            ex[i]=j;
            po=i;//更新po的位置
        }
    }
}
	//scanf("%lld%lld",&n,&m);
	//printf("%lld\n",ans);
	//for(int i=1;i<=n;i++)
int main()
{
    int n;  cin>>n;
    for(int i=0;i<n;i++)    cin>>s[i];
    for(int i=0;i<(int)s[0].size();i++)
        ans[cnt++]=s[0][i]; ans[cnt]='\0';
    for(int i=1;i<n;i++){
        m=0;
        for(int j=0;j<(int)s[i].size();j++)  tmp[m++]=s[i][j];
        tmp[m]='\0';
        EXKMP(ans,tmp,cnt,m);
        int st=0;
        for(int j=max(0,cnt-m);j<cnt;j++){
            if(j+ex[j]==cnt){
                st=ex[j];
                break;
            }
        }
        for(int j=st;j<m;j++)   ans[cnt++]=tmp[j];
    }
    for(int i=0;i<cnt;i++)  cout<<ans[i];
}

D. White Lines
题意:
选择覆盖 k ∗ k k*k kk的区域使得这个区域全为白色,使得一行或一列全为白色的数目最多,输出最多的数目。

题解:(二维滑动窗口)
如果使得一行全为白色有两种情况:
第一种:本身即为白色行
第二钟:一行中最左面的黑色点和最右面的黑色点都在 k ∗ k k*k kk的区域内

#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;

const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
char maps[2005][2005];
int ans[2005][2005],l[N],r[N],u[N],d[N];
	//scanf("%lld%lld",&n,&m);
	//printf("%lld\n",ans);
	//for(int i=1;i<=n;i++)
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",maps[i]+1);
        for(int j=1;j<=n;j++)
        if(maps[i][j]=='B')
        {
            if(l[i]==0) l[i]=j;
            if(u[j]==0) u[j]=i;
            r[i]=j;
            d[j]=i;
        }
    }
    int base_cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(l[i]==0)
           base_cnt++;
        if(u[i]==0)
            base_cnt++;
    }

    for(int i=1;i<=n;i++)///二维滑动窗口
    {
        int temp_cnt=0;
        for(int j=1;j<=n;j++)
        {
            if(i>=d[j]&&i-k+1<=u[j]&&u[j]!=0)///可以被覆盖
                temp_cnt++;
            if(j>k&&i>=d[j-k]&&i-k+1<=u[j-k]&&u[j-k]!=0)///滑动
                temp_cnt--;
            ans[i][j]+=temp_cnt;
        }
    }

    for(int j=1;j<=n;j++)
    {
        int temp_cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(j>=r[i]&&j-k+1<=l[i]&&l[i]!=0)
                temp_cnt++;
            if(i>k&&j>=r[i-k]&&j-k+1<=l[i-k]&&l[i-k]!=0)
                temp_cnt--;
            ans[i][j]+=temp_cnt;
        }
    }
    int ppp=0;
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            ppp = max(ppp,ans[i][j]);
            //if(ppp==2) printf("%d %d ",i,j);
        }
    }
    printf("%d",ppp+base_cnt);
}

C. Powers Of Two
题解:会使容器就好办了
模拟一下数的分解操作,例如:8就可以分解为8个二进制数。

#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;

const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;

int a[N],b[N],c[N];
	//scanf("%lld%lld",&n,&m);
	//printf("%lld\n",ans);
	//for(int i=1;i<=n;i++)
int main()
{
    scanf("%d%d",&n,&k);
    if(m>n) return printf("NO\n"),0;
    priority_queue<int>Q;
    for(int i=0;i<32;i++)
    {
        if(((1<<i)&n)>0)
            Q.push(i);
    }
    if(Q.size()>k) return printf("NO\n"),0;
    printf("YES\n");
    while(Q.top()>0&&Q.size()<k)
    {
        int lim=Q.top();
        Q.pop();
        Q.push(lim-1);
        Q.push(lim-1);
    }
    while(Q.size()>0)
    {
        cout<<(1<<Q.top())<<" ";
		Q.pop();
    }
}

D. Circular Dance
题意:
有n个人连接一个单向环,给出第 i 个人的下一个人和下下个人的编号(有可能是反的),求出这n个人构成的环。
题解:
由于给出的下一人和下下一人的标号,所以可知这两人挨着,所以可以知道任意一个人挨着的两个人,所以可以以任意一个人开头,然后找下去谁和他相互挨着。
注意:就是环找出来了但不一定是顺时针,所以判断一下顺序!!!

#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;

const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
vector<int>a[N];
int ans[N];
	//scanf("%lld%lld",&n,&m);
	//printf("%lld\n",ans);
	//for(int i=1;i<=n;i++)

int main()
{
    scanf("%d",&n);
    int x1,y1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(i==1) x1=x,y1=y;
        a[x].push_back(y);
        a[y].push_back(x);
    }
    ans[0]=a[1][0];
    ans[1]=1;
    ans[2]=a[1][1];
    for(int i=3;i<n;i++)
    {
        if(a[ans[i-1]][0]==ans[i-2])
        ans[i]=a[ans[i-1]][1];
        else
        ans[i]=a[ans[i-1]][0];
    }
    if(ans[2]==x1||ans[2]==y1)//判断是否为顺时针
    	for(int i=0;i<n;i++)
        	printf("%d ",ans[i]);
    else
    	for(int i=n-1;i>=0;i--)
        	printf("%d ",ans[i]);
}

E. Almost Regular Bracket Sequence
题意:
给你一个长度为n的括号串,你可以独立的选择一个位置,改变那个位置的括号之后,如果总括号串是平衡的,这就是一个合法位置,求一共有多少个合法的位置。

题解:思维题
正常来说,区间里的 ( 的数目要大于等于 ) ,结束时 ( 等于 ) ,才可以构成合法括号。
根据题意可以发现,修改 ( 和修改 ) 的情况是对称的,所以只需要考虑修改一类括号然后另一种情况,逆着推就好了。

所以可以考虑修改),当应该修改)时,由题意可知)数目一定比(数目多两个,举个例子 :(()))),容易发现规律:满足 a n s [ i ] ans[i] ans[i]>-2的 )都可以翻转过来,总可以找到)构成合法括号,因为 i 前面的括号都一定是匹配的。
再考虑:))(()) ,只能翻转第一个),他的 a n s [ i ] ans[i] ans[i]=-1,很显然不能翻转第二个 a n s [ i ] ans[i] ans[i]=-1的),因为它前面的括号不是全部匹配的。所以可以知道第一个符合 a n s [ i ] ans[i] ans[i]为-1的)一定为最后一个不匹配括号,因为如果这个)后还有不匹配的组合,一定不可能通过一步修改完成。所以 a n s [ i ] ans[i] ans[i]=-1为循环的终止条件。说的不是很清楚,详见代码。

#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;

const int N=1e6+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;

int ans[N],ppp[N];///ans 记录右括号 ppp 记录左括号
char a[N];
	//scanf("%lld%lld",&n,&m);
	//printf("%lld\n",ans);
	//for(int i=1;i<=n;i++)

int main()
{
    scanf("%d",&n);
    scanf("%s",a);
    int flag=0;
    for(int i=0;i<n;i++)
    {
        if(a[i]=='(') cnt++;
        else cnt--;
        ans[i]=cnt;
        if(ans[i]<-2) flag=1;
    }
    for(int i=n-1;i>=0;i--)
    {
        if(a[i]==')') q++;
        else q--;
        ppp[i]=q;
        if(ppp[i]<-2) flag=1;
    }

    int len=0;
    if(cnt!=2&&cnt!=-2||flag==1)
        return printf("0\n"),0;
    else if(cnt==2)
    {
        for(int i=n-1;i>=0;i--)
        {
            if(a[i]=='('&&ppp[i]>-2)
               len++;
            if(ppp[i]==-1) break;
        }
    }
    else if(cnt==-2)
    {
        for(int i=0;i<n;i++)
        {
            if(a[i]==')'&&ans[i]>-2)
               len++;
            if(ans[i]==-1) break;
        }
    }
    printf("%d\n",len);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值