扩展欧拉降幂,维护不同模phi下的ans,观察可得后面一堆phi是2的指数,当指数很大时取模都是1,所以只要维护8层phi就好了
?一个显然的结论:每个位置最多被两个区间覆盖
?所有区间按照右端点从小到大排序
?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; }
这题主要是结论难得找,找到结论后就是一个数位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; }
问交换两个字符后成为双回文的方案数,枚举切断位置,判断左右是否需要交换,再找以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; }
原文:https://www.cnblogs.com/Hetui/p/9397861.html