2021年广东工业大学第十五届文远知行杯程序设计竞赛(题解

A    M形字符串:

判断回文,字符串是否相同,都可以采用字符串哈希O(1)判断。

题目要计算的是前缀也就是确定了左端点1,所以枚举右端点i。判断1~i (后面这种形式都是表示L-R的字符串子串) 是不是回文,如果是回文,就可以在他下一位置i+1~i+i是不是和1~i相同,如果是那就可以拼成一个M字符串,同样用哈希值判断。那M字符串中两个相同回文重叠的情况呢?只要多判一个位置,也就是i~i+i-1是不是和1~i相同,那样也会拼成一个M字符串。

简单证明一下为什么这样是对的:( 只要判断1~i是否和i~i+i-1以及i+1~i+i相同 )

对于第一种情况(首尾不重复直接拼起来)得到的M字符串长度是2*i,对于第一种情况(首尾重复一个)得到的M字符串长度是2*i-1。

又因为i是从1~n递增的,所以也就判断所有M字符串长度的情况,而且只判断了一次。

代码:

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e5+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
char s[N];
ll p[N],pre[N],suf[N];
int n;
void wo()
{
    p[0]=1;
    fu(i,1,n)
    {
        pre[i]=pre[i-1]*base+s[i]-'a'+1;
        suf[i]=suf[i-1]*base+s[n-i+1]-'a'+1;
        p[i]=p[i-1]*base;
    }
}
ll g1(int l,int r) {return pre[r]-pre[l-1]*p[r-l+1];}
ll g2(int l,int r) {return suf[r]-suf[l-1]*p[r-l+1];}
bool check(int i,int j) {return g1(i,j)==g2(n-j+1,n-i+1);}
int main() 
{
    cin>>(s+1);
    n=strlen(s+1); wo();
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(check(1,i))//1~i是回文
        {
            int len=i;
            if(g1(1,i)==g1(i+1,i+len)) ans++;//判断2个子串相同
            if(g1(1,i)==g1(i,i+len-1)) ans++;
            //printf("%d-%d\n",i,ans);
        }
    }
    cout<<ans<<endl;
    return 0;
}

B    找山坡:

类似单调栈维护矩阵最大面积题目,这里只是多了要在相等的时候更新答案~

代码:

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=1e6+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
int st[N],tt,w[N];
int main() 
{
    int n;cin>>n;
    int ans=0;
    fu(i,1,n)
    {
        int x;cin>>x;
        int tmp=0;
        while(tt&&st[tt]>x)
        {
            tmp+=w[tt];
            tt--;
        }
        if(!tt) tmp=0;//栈空了,就清空累加
        if(tt&&st[tt]==x) w[tt]+=tmp+1,ans=max(ans,w[tt]-1);
        else st[++tt]=x,w[tt]=tmp+1;
    }
    cout<<ans<<endl;
    return 0;
}

C    涂墙:

问任意5个平方数是否能拼成n,可重复用平方数(比赛时看错了,卡了一年),打表感觉比较大的数都可行。比较小的数直接记忆化搜索就行。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=1e6+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
int y[N][10],wan[N];
int p[N];
bool dfs(int n,int c)
{
    if(y[n][c]!=-1) return y[n][c];
    if(c==1) return y[n][1]=wan[n];
    for(int i=1;i<=1000&&p[i]<=n;i++) 
    {
        if(dfs(n-p[i],c-1)) return y[n][c]=1;
    }
    return y[n][c]=0;
}
int main() 
{
    int t;cin>>t;
    ms(y,-1);
    for(int i=1;i<=1000;i++) p[i]=i*i,wan[p[i]]=1;
    while(t--)
    {
        int n;cin>>n;
        if(n>1000) puts("YES");
        else puts(dfs(n,5)?"YES":"NO");
    }
    return 0;
}

D    动态序列:

模拟,加个全局数组的偏移量,乘法标记mul,加和标记add。操作1,mul和add都乘b。操作2,add加b。操作5是找到指定位置的数x,答案就是x*mul+add。

操作3和4都是往数组加入数,加入数的话,因为最后输出他的话是按x*mul+add,所以插入的时候应该反着来,(x-add)/mul,这样来消除之前偏移量的影响。

插入数组可以维护2个vector,插入后面,就直接放在当前数组后面,插入前面,可以放入另一个vector后面,到时候反着找。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e5+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
vector<ll> a,f;
ll mp(ll x,ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1) res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
int main() 
{
    int n,q;cin>>n>>q;
    fu(i,1,n) 
    {
        int x;cin>>x;
        a.push_back(x);
    }
    ll mul=1,add=0,l=0,r=0;
    while(q--)
    {
        ll op,b;cin>>op>>b;
        if(op==1) mul=mul*b%mod,add=add*b%mod;
        if(op==2) add=(add+b)%mod;
        if(op==3) f.push_back((b-add+mod)%mod*mp(mul,mod-2)%mod);
        if(op==4) a.push_back((b-add+mod)%mod*mp(mul,mod-2)%mod);
        if(op==5)
        {
            ll x;
            int flen=f.size();
            if(flen>=b) x=f[flen-b];
            else b-=flen,x=a[b-1];
            cout<<(x*mul+add)%mod<<endl;
        }
    }
    return 0;
}

E   捡贝壳:

对每个a[i]都分解因数,每个因数x都是一个块,来存放x倍数的下标。对每个块排序,询问l-r里x倍数个数时,直接在x的块里面二分查找l-r包含的下标个数。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=1e5+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
vector<int> pool[N];
int main() 
{
    int n,m;cin>>n>>m;
    fu(i,1,n)
    {
        int x;cin>>x;
        for(int j=1;j<=sqrt(x);j++)
        {
            if(x%j==0) 
            {
                pool[j].push_back(i);
                if(j*j!=x)
                pool[x/j].push_back(i);
            }
        }
    }
    fu(i,1,N-1) sort(pool[i].begin(),pool[i].end());
    while(m--)
    {
        int l,r,x;cin>>l>>r>>x;
        cout<<upper_bound(pool[x].begin(),pool[x].end(),r)
        -lower_bound(pool[x].begin(),pool[x].end(),l)<<endl;
    }
    return 0;
}

F   钓卡游戏:待补

G   分割:

求面积就是求∑(x[j]-x[i])*∑(y[k]-y[l]),相当于暴力枚举端点。对于这个式子,可以x和y分开求。

比如求∑(x[j]-x[i]),就是在数轴上有1~n个点,求任意2个端点距离的和。很经典的问题,只需考虑相邻两个点之间的边被"用过"多少次,就是该边左右端点数的乘积。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e7+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
ll x[N],y[N],sx[N],sy[N];
int main() 
{
    int n,m;cin>>n>>m;
    fu(i,1,n) cin>>x[i];
    fu(i,1,m) cin>>y[i];
    sort(x+1,x+n+1);sort(y+1,y+m+1);
    ll ax=0,ay=0;
    fu(i,1,n-1)
    {
        ll now=x[i+1]-x[i];
        ax=(ax+now*i%mod*(n-i))%mod;
    }
    fu(i,1,m-1)
    {
        ll now=y[i+1]-y[i];
        ay=(ay+now*i%mod*(m-i))%mod;
    }
    cout<<ax*ay%mod<<endl;
    return 0;
}

H     有多短:

官方题解:(偷懒)

  • 先说结论:答案是 2*k/(度数为1的节点个数)
  • 感性认识:
    • 直径肯定是从一个度数1的点到另一个度数1的点
    • 所以我们只给所有度数为1相连的边分配权值,其他边全置为0(之后不再考虑)
    • 均摊下来,每条边的权值Wi = k/(度数为1的节点个数)
    • 所以直径为2*Wi

代码:

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e7+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
int d[N];
int main() 
{
    int n,p=0;
    double k;
    cin>>n>>k;
    fu(i,1,n-1)
    {
        int u,v;cin>>u>>v;
        d[u]++,d[v]++;
    }
    fu(i,1,n) if(d[i]==1) p++;
    printf("%.6f\n",k*2/p);
    return 0;
}

I   母牛哥与子序列:

考虑从一个空序列出发,每加入一个数,对答案贡献是怎么变化,就变成了一道简单的dp题。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e7+99;
const ll INF=1e9+7;
const ll base=13331;
const ll mod=1e9+7;
ll dp[N];
int main() 
{
    int n;cin>>n;
    fu(i,1,n) 
    {
        int x;cin>>x;
        dp[i]=(dp[i-1]*(1+x)+x)%mod;
    }
    cout<<dp[n]<<endl;
    return 0;
}

J     最长连续相同数字的长度:应该是splay,之后补。

posted on 2021-03-28 21:11  Aminers  阅读(163)  评论(0)    收藏  举报

导航