牛客网多校第四场

A:Ternary String

扩展欧拉降幂,维护不同模phi下的ans,观察可得后面一堆phi是2的指数,当指数很大时取模都是1,所以只要维护8层phi就好了

 

B:Interval Revisited

⼀一个显然的结论:每个位置最多被两个区间覆盖

•所有区间按照右端点从小到大排序

•dp(i, j)表示第i个区间必选,上⼀一次被覆盖⼀一次的位置是j,[1, j]覆盖权值和最⼤大值 的最小值

•dp(i, j) = min(max(dp(k, l), w(i) + w(k)) R(k) +1 >= L(i) and L(k) -1 <= l < L(i) •j和R(k)有关

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std;
struct node
{
    int le,ri,wi;
};
node seg[2010];
int n,m;
int dp[2010][2010];

void init()
{
    memset(dp,0x3f,sizeof(dp));
}

bool cmp(const node &a,const node &b)
{
    if(a.ri==b.ri) return a.wi<b.wi;
    return a.ri<b.ri;
}

int main()
{
    int i,j;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%d%d%d",&seg[i].le,&seg[i].ri,&seg[i].wi);
        }
        init();
        sort(seg+1,seg+1+n,cmp);

        int ans=dp[0][0];
        for(i=1;i<=n;i++)
        {
            if(seg[i].le==1) dp[i][0]=seg[i].wi;
            for(j=1;j<=i;j++)
            {
                if(seg[j].ri<seg[i].le-1) continue;
                int maxx=dp[j][seg[i].le-1];
                if (seg[j].ri>=seg[i].le) maxx=max(maxx,seg[i].wi+seg[j].wi);
                else maxx=max(maxx,max(seg[i].wi,seg[j].wi));
                dp[i][seg[j].ri]=min(dp[i][seg[j].ri],maxx);
            }
            for(j=seg[i].le;j<=seg[i].ri;j++)
            {
                dp[i][j]=min(dp[i][j],dp[i][j-1]);
            }
            if(seg[i].ri==m) ans=min(ans,dp[i][m]);
        }
        if(ans==0x3f3f3f3f) ans=-1;
        printf("%d\n",ans);
    }
    return 0;
}
View Code

C:Chiaki Sequence Reloaded

这题主要是结论难得找,找到结论后就是一个数位DP板题了

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std;
const long long mod=1e9+7;
long long n;
long long dp[70][150][4];
int num[70],len;


//pre表示上一个填的啥,pre=0表示还没填任何数(没出现第一个1).
//state表示相邻对有多少相同和不同,因为可能为负,所以加上偏移量66
long long dfs(int pos,int state,int lim,int pre)
{
   // cout<<pos<<" "<<state<<endl;
    if(pos==-1) return abs(state-66);
    if(!lim&&dp[pos][state][pre]!=-1) return dp[pos][state][pre];
    int up=lim?num[pos]:1;
    long long cnt=0;
    for(int i=0;i<=up;i++)
    {
        if(pre==2)
        {
            if(i==1) cnt+=dfs(pos-1,state,lim&&i==up,i);
            else cnt+=dfs(pos-1,state,lim&&i==up,pre);
        }
        else cnt+=dfs(pos-1,state+(i==pre?1:-1),lim&&i==up,i);
    }
    cnt%=mod;
    if(!lim) dp[pos][state][pre]=cnt;
    return cnt;
}

long long solve(long long a)
{
    len=0;
    while(a>0)
    {
        num[len++]=a%2;
        a/=2;
    }
    return dfs(len-1,66,1,2);
}

int main()
{
    int T;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&n);
        printf("%lld\n",solve(n));
    }
    return 0;
}
View Code

 

D:Another Distinct Values

 

F:Beautiful Garden

 

G:Maximum Mode

 

H:Double Palindrome

问交换两个字符后成为双回文的方案数,枚举切断位置,判断左右是否需要交换,再找以i为结尾的前缀和串的最长匹配和以i为起始的后缀和串的最长后缀匹配,从而确定需要交换的位置。若交换满足条件,则最多两个字符不同,其间的字符用hash判断。当左右都为回文串时,注意奇数长回文串中心可换,打上flag标记证明所有相同字符可换,最后统一计算。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<set>
#include<vector>
#include<map>
#include<queue>
using namespace std;

const int MAX=2e5+5;
const int base=163;
const int mod=1e9+7;

unsigned long long p[MAX],Hashx[MAX],Hashy[MAX];
char s[MAX],str[MAX]; //原字符串 加#字符串 
int Len[MAX],len;   //回文数组 数组长度
pair<int,int> PII;
set<pair<int,int>> S;

int t,n;
int pre[MAX],suf[MAX],Nxt[MAX],c[MAX];
long long ans;

void init(){//处理Hash值
    Hashx[0]=0,Hashy[n+1]=0;
    for(int i=1;i<=n;i++) Hashx[i]=Hashx[i-1]*base+(s[i]-'a');
    for(int i=n;i>=1;i--) Hashy[i]=Hashy[i+1]*base+(s[i]-'a');
}
 
unsigned long long get1(int l, int r){//取出g里l - r里面的字符串的Hash值
    return Hashx[r]-Hashx[l-1]*p[r-l+1];
}

unsigned long long get2(int l, int r){//取出g里l - r里面的字符串的Hash值
    return Hashy[l]-Hashy[r+1]*p[r-l+1];
}

int check(int l1,int r1,int l2,int r2){
    if(l1>r1&&l2>r2) return 1; 
    unsigned long long Hash1=get1(l1,r1);
    unsigned long long Hash2=get2(l2,r2);
    return Hash1==Hash2;
}

void getstr(void){    //字符串加入# 
    int i,l,k=0;
    
    memset(str,0,sizeof(str));
    l=strlen(s+1);
    str[k++]='$';
    for(i=1;i<=l;i++){
        str[k++]='#';
        str[k++]=s[i];
    }
    str[k]='#';
    len=k;
}

void Manacher(void){
    int i;
    int mx=0,id;
    
    getstr();
    for(i=1;i<len;i++){
        if(mx>i)
            Len[i]=min(Len[2*id-i],mx-i);  //2*id-i是i关于id的对称点 
        else   //越过mx则暴力判断 
            Len[i]=1;
        while(str[i+Len[i]]==str[i-Len[i]])  //回文匹配 
            Len[i]++;
        if(Len[i]+i>mx){    //更新mx和id 
            mx=Len[i]+i;
            id=i;
        }
    }
}

int check(int L,int R){
    L<<=1;R<<=1;
    return 2*Len[(L+R)>>1]-1>=R-L+1;
}

void GetNext(char T[], int next[]){
    int i,j;
    int t_len=strlen(T);
    int a,p;

    next[0]=t_len;
    for(i=1,j=-1;i<=t_len;i++,j--){
        if(j<0||i+next[i-a]>=p){
            if(j<0)
                p = i, j = 0;
            while(p<=t_len&&T[p]==T[j])
                p++,j++;
            next[i]=j;
            a=i;
        }
        else
            next[i]=next[i-a];
    }
}

/* 求解extend[] */
//extend[i]等于S[i]...S[n-1]与T的最长公共前缀的长度
void GetExtend(char S[], char T[], int extend[], int next[]){
    int i,j;
    int a;             
    int p;             //记录匹配成功的字符的最远位置p,及起始位置a
    int s_len=strlen(S+1);
    int t_len=strlen(T+1);

    GetNext(T,next);  //得到next
    for(i=0,j=-1;i<=s_len;i++,j--){  //j即等于p与i的距离,其作用是判断i是否大于p(如果j<0,则i大于p)
        if (j<0||i+next[i-a]>=p){  //i大于p(其实j最小只可以到-1,j<0的写法方便读者理解程序),或者可以继续比较(之所以使用大于等于而不用等于也是为了方便读者理解程序)
            if (j<0)
                p=i,j=0;  //如果i大于p
            while(p<=s_len&&j<=t_len&&S[p]==T[j])
                p++,j++;
            extend[i+1]=j;
            a=i;
        }
        else
            extend[i+1]=next[i-a];
    }
}

void add(int l,int r){
    if(l==r) return ;
    if(l>r) swap(l,r);
    PII=make_pair(l,r);
    if(!S.count(PII)){
        S.insert(PII);
        ans++;
    }
}

int main(){
    int i;
    
    scanf("%d",&t);
    p[0]=1;
    for(int i=1;i<=MAX;i++) p[i]=p[i-1]*base;
    while(t--){
        S.clear();
        ans=0;
        scanf("%s",s+1);
        n=strlen(s+1);
        init();Manacher();
        for(i=1;i<=n;i++) str[i]=s[n-i+1];
        str[0]=str[n+1]=0;
        GetExtend(s+1,str+1,suf,Nxt);GetExtend(str+1,s+1,pre,Nxt);
        for(i=1;i<=n/2;i++) swap(pre[i],pre[n-i+1]);
        int flag=0;
        for(i=1;i<=n-1;i++){
            int L=check(1,i),R=check(i+1,n);
            int l1,l2,r1,r2;
            int f1=0,f0=0;
            if(L&&R){
                flag=1;
                if((i&1)&&((n-i)&1)&&s[(i+1)/2]!=s[(n+i+1)/2])
                    add((i+1)/2,(n+i+1)/2);
            }
            else{
                if(R){
                    l1=(i+1)/2-Len[i+1]/2; r1=i+1-l1;
                    l2=pre[i]+1; r2=i+1-l2;
                    if(l1==l2) f1=1;
                    else if(check(l2+1,l1-1,r1+1,r2-1)) f0=1;
                }
                else if(L){
                    l1=(n+i+1)/2-Len[n+i+1]/2; r1=n+i+1-l1;
                    r2=n-suf[i+1]; l2=n+i+1-r2;
                    if(l1==l2) f1=1;
                    else if(check(l2+1,l1-1,r1+1,r2-1)) f0=1;
                }
                else{
                    l1=(i+1)/2-Len[i+1]/2; r1=i+1-l1;
                    l2=(n+i+1)/2-Len[n+i+1]/2; r2=n+i+1-l2;
                    if(check(1,l1-1,r1+1,i)&&check(i+1,l2-1,r2+1,n)) f0=1;
                }
            }
            if(f1){
                if((i&1)&&s[(i+1)/2]==s[r1])
                    add(l1,(i+1)/2);
                if((i&1)&&s[(i+1)/2]==s[l1])
                    add((i+1)/2,r1);
                if(((n-i)&1)&&s[(n+i+1)/2]==s[r1])
                    add(l1,(n+i+1)/2);
                if(((n-i)&1)&&s[(n+i+1)/2]==s[l1])
                    add(r1,(n+i+1)/2);
            }
            if(f0){
                if(s[l1]==s[l2]&&s[r1]==s[r2]){
                    add(l1,r2);
                    add(l2,r1);
                }
                if(s[l1]==s[r2]&&s[l2]==s[r1]){
                    add(l2,l1);
                    add(r1,r2);
                }
            }
        }
        if(flag){
            for(i=1;i<=n;i++) c[s[i]-'a']++;
            for(i=0;i<26;i++){
                ans+=1ll*c[i]*(c[i]-1)/2;
                c[i]=0;
            }
        }
        printf("%lld\n",ans);
    }
    
    return 0;
} 
View Code

 

J:Hash Function

读入时把没有碰撞的下标和值push到优先队列里,每取出一个值二分寻找能够放入的下一个下标,二分两次保证没有矛盾。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <set>
using namespace std;

const int MAX=2e5+5;
int t,n,a[MAX],ans[MAX];
int tot,m;
set<int> S;

int main(){
    int i;
    
    scanf("%d",&t);
    while(t--){
        tot=m=0;
        S.clear();
        priority_queue<pair<int,int>> Q;
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d",&a[i]);
            if(a[i]!=-1) tot++;
            if(a[i]%n==i) Q.push(make_pair(-a[i],i));
            S.insert(i);
        }
        while(!Q.empty()){
            int to=Q.top().second;
            Q.pop();
            if(!S.count(to)) continue;
            S.erase(to);
            ans[m++]=a[to];
            if(!S.empty()){
                auto nxt=S.upper_bound(to);
                if(nxt==S.end()) nxt=S.begin();
                auto nxt2=S.lower_bound(a[*nxt]%n);
                if(nxt2==S.end()) nxt2=S.begin();
                if(a[*nxt]!=-1&&nxt==nxt2)
                    Q.push(make_pair(-a[*nxt],*nxt));
            }
        }
        if(tot!=m) puts("-1");
        else if(tot==0) puts("");
        else{
            for(i=0;i<m;i++)
                printf("%d%c",ans[i],i==m-1?'\n':' ');
        }
    }
    
    return 0;
} 
View Code
posted @ 2018-07-31 19:38  Hetui  阅读(523)  评论(3编辑  收藏  举报