题意:找一个最大的回文后缀。
题解:随便hash一下。
#include<bits/stdc++.h>
using namespace std;
const int N = 400005;
struct hashMap{
typedef unsigned long long ull;
const ull base = 233;
ull h[N], pw[N];
void getHash(char* s){
int n=strlen(s+1);
pw[0]=1;h[0]=0;
for(int i=1;i<=n;++i){
h[i]=h[i-1]*base+s[i]-'a';
pw[i]=pw[i-1]*base;
}
}
ull getVal(int l, int r){
return h[r]-h[l-1]*pw[r-l+1];
}
}hs, hs1;
char s[N], s1[N];
int n;
int ans[2];
int main(){
cin>>n;
cin>>s+1;
for(int i=n;i>=1;--i){
s1[n-i+1]=s[i];
}
hs.getHash(s);
hs1.getHash(s1);
int len=0;
for(int i=n;i>=1;--i){
int t=n-i+1;
if(t>i-1)break;
if(hs.getVal(i,n)==hs1.getVal(n-i+2,n-(i-t)+1)){
len=t;
}
}
ans[0]=n-2*len;
len=0;
for(int i=n;i>=1;--i){
int t=n-i;
if(t>i-1)break;
if(hs.getVal(i+1,n)==hs1.getVal(n-i+2,n-i+t+1)){
len=t;
}
}
ans[1]=n-(2*len+1);
cout<<min(ans[0],ans[1]);
return 0;
}
题意:枚举区间,求区间gcd*区间max的和。
题解:考虑枚举最大值,这里采用笛卡尔树。原因是笛卡尔树采用大根堆结构的时候,我们能够轻易的知道,每一个最大值能够主导的区间。我们知道每一个最大值能够主导的区间之后,现在要考虑的是,这个区间内的gcd情况。首先我们要知道一个结论(比较直观),包含这个值,区间向左或者右延展的时候,区间gcd是递减的(非常直观),且取值最多有gcd个(每次至少少一个质因数)。我们只需要得到每一个gcd的值和区间长度(这个可以用st表二分查询)就可以得到答案。最终在O(nlogn^2)的时间就可以做完这道题。
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
const int MOD = 1e9+7;
int a[N], n, ans;
int st[N], top, ls[N], rs[N];
inline int gcd(int a, int b){return b==0?a:gcd(b,a%b);}
struct RMQ{
int lg[N], mn[N][21];
void build(){
for(int i=1;i<=n;++i)mn[i][0]=a[i];
for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=20;++i){
for(int j=1;j+(1<<i)-1<=n;++j){
mn[j][i]=gcd(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);/*bug*/
}
}
}
int query(int l, int r){
int len=lg[r-l+1];
return gcd(mn[l][len],mn[r-(1<<len)+1][len]);
}
}table;
int L[N], R[N], tL[N], tR[N];
void solve(int nd, int l, int r){
if(ls[nd])solve(ls[nd],l,nd-1);
if(rs[nd])solve(rs[nd],nd+1,r);
L[0]=R[0]=0;
int x=nd, y=nd;
while(x>=l){
int g=table.query(x,nd);
L[++L[0]]=g;
int ll=l, rr=x, mid;
int id=x;
while(ll<=rr){
mid=ll+rr>>1;
if(table.query(mid,nd)==L[L[0]]){
id=mid;
rr=mid-1;
}else{
ll=mid+1;
}
}
tL[L[0]]=x-id+1;
x=id-1;
}
while(y<=r){
int g=table.query(nd,y);
R[++R[0]]=g;
int ll=y, rr=r, mid;
int id=y;
while(ll<=rr){
mid=ll+rr>>1;
if(table.query(nd,mid)==R[R[0]]){
id=mid;
ll=mid+1;
}else{
rr=mid-1;
}
}
tR[R[0]]=id-y+1;
y=id+1;
}
int res=0;
for(int i=1;i<=L[0];++i){
for(int j=1;j<=R[0];++j){
int g=gcd(L[i],R[j]);
res+=1ll*g*tL[i]%MOD*tR[j]%MOD;
res%=MOD;
}
}
ans+=1ll*res*a[nd]%MOD;
ans%=MOD;
}
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
table.build();
for(int i=1;i<=n;++i){
while(top&&a[st[top]]<a[i])ls[i]=st[top--];
if(top)rs[st[top]]=i;
st[++top]=i;
}
solve(st[1],1,n);
cout<<ans;
return 0;
}
题意:你现在需要去组织一个长度为n的只包含小写字母的字符串,使得这个字符串中不能出现给定的q个禁忌字符串。
题解:首先,我们将trie树建出来。然后可以想到,在匹配过程中,树上有一些节点是不合法的。这就是那些本身是结束节点,或者自己的后缀是结束节点(由于建立fail树的时候,是按照BFS序去建立的,所以可以直接对这些不合法的节点打上标记)。然后,我们可以将带有fail指针的trie树看成是一个有向图。这样,问题就能转化为:在一个有向图中,从0节点出发,走k步,到达某一个节点的方案数。这个可以通过矩阵的快速幂来解决。
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
const int MOD = 1e9+7;
const int M = 26;
struct trieNode{
int son[M], mark, fail;
}T[N];
int cntNode;
void Insert(char* s){
int len=strlen(s+1);
int cur=0;
for(int i=1;i<=len;++i){
if(!T[cur].son[s[i]-'a']){
T[cur].son[s[i]-'a']=++cntNode;
}
cur=T[cur].son[s[i]-'a'];
}
T[cur].mark=1;
}
struct m{
int a[105][105];
m operator*(m m1){
m ret;
for(int i=0;i<=cntNode;++i){
for(int j=0;j<=cntNode;++j){
ret.a[i][j]=0;
for(int k=0;k<=cntNode;++k){
ret.a[i][j]+=1ll*a[i][k]*m1.a[k][j]%MOD;
ret.a[i][j]%=MOD;
}
}
}
return ret;
}
}gkd;
m powmod(m m1, int k){
m ret;
memset(ret.a,0,sizeof ret.a);
for(int i=0;i<=cntNode;++i)ret.a[i][i]=1;
while(k){
if(k&1)ret=ret*m1;
m1=m1*m1;
k>>=1;
}
return ret;
}
void getFail(){
queue<int> q;
for(int i=0;i<26;++i){
int nd=T[0].son[i];
if(nd){
q.push(nd);
T[nd].fail=0;
}
}
while(!q.empty()){
int nd=q.front();
q.pop();
if(T[T[nd].fail].mark>0)T[nd].mark=1;
for(int i=0;i<26;++i){
int& nd1=T[nd].son[i];
if(!nd1){
nd1=T[T[nd].fail].son[i];
}else{
T[nd1].fail=T[T[nd].fail].son[i];
q.push(nd1);
}
}
}
}
void build(){
for(int i=0;i<=cntNode;++i){
for(int j=0;j<26;++j){
int nd=T[i].son[j];
if(T[i].mark==0&&T[nd].mark==0){
gkd.a[i][nd]++;
}
}
}
}
char s[N];
int n, q;
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cin>>n>>q;
for(int i=1;i<=q;++i){
int t;cin>>t;
cin>>s+1;
Insert(s);
}
getFail();
build();
m res=powmod(gkd,n);
int ans=0;
for(int i=0;i<=cntNode;++i){
ans+=res.a[0][i];
ans%=MOD;
}
cout<<ans;
return 0;
}
题意:给你一根过原点的直线和平面直角坐标系中n个点,你现在需要在直线上选择一个点,使得过这个点的一个固定大小直径的圆能覆盖最多的点。
题解:首先过每一个点,作一个大小为R的圆。然后,我们需要的仅仅是原点到这两个点的有向线段长度而已。丢进一个容器里,排序后扫描线。
#include<bits/stdc++.h>
using namespace std;
const int N = 300005;
#define all(x) x.begin(), x.end()
const double eps = 1e-3;
struct P{
double x, y;
P(){}
P(double _x, double _y){
x=_x;
y=_y;
}
}pts[N];
struct Node{
double x;
int o;
bool operator<(const Node& rhs)const{
if(fabs(x-rhs.x)<1e-10)return o>rhs.o;
else return x<rhs.x;
}
};
double cross(P p1, P p2){
return p1.x*p2.y-p1.y*p2.x;
}
double dot(P p1, P p2){
return p1.x*p2.x+p1.y*p2.y;
}
int n;
double r, a, b;
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cin>>n>>r>>a>>b;
r+=eps;
for(int i=1;i<=n;++i){
double x,y;
cin>>x>>y;
pts[i]=(P){x,y};
}
P v1=P(a,b);
vector<Node> points;
double len=sqrt(a*a+b*b);
if(len<eps){
int cnt=0;
for(int i=1;i<=n;++i){
if(pts[i].x*pts[i].x+pts[i].y*pts[i].y<=r*r)++cnt;
}
cout<<cnt;
return 0;
}
for(int i=1;i<=n;++i){
P v2=P(pts[i].x, pts[i].y);
double t1=dot(v1,v2)/len;
double t2=cross(v1,v2)/len;
if(fabs(t2)-r>0)continue;
double delta=sqrt(r*r-t2*t2);
double ll=t1-delta, rr=t1+delta;
points.push_back((Node){ll,1});
points.push_back((Node){rr,-1});
}
sort(all(points));
int t=0;
int res=0;
for(auto& p:points){
t+=p.o;
res=max(res,t);
}
cout<<res;
return 0;
}
题意:n个数字,从中选取k个。使得它们的二进制按位与之后得到的结果最大。
题解:首先,从高位到低位考虑的时候,如果某一位对应的数字个数超过k个,那剩下的就会被丢弃。然后问题就被转化为一个子问题了,模拟一下就能过。
#include<bits/stdc++.h>
using namespace std;
int vis[200005];
int a[200005][32];
int n, k;
int main(){
cin>>n>>k;
for(int i=1;i<=n;++i){
int x;cin>>x;
for(int j=0;j<=29;++j){
if(x>>j&1){
a[i][j]=1;
}
}
}
for(int i=29;i>=0;--i){
int cnt=0;
for(int j=1;j<=n;++j){
if(!vis[j]&&a[j][i])++cnt;
}
if(cnt>=k){
for(int j=1;j<=n;++j){
if(!vis[j]&&!a[j][i])vis[j]=1;
}
}
}
int res=-1;
for(int i=1;i<=n;++i){
if(!vis[i]){
int t=0;
for(int j=0;j<=29;++j){
if(a[i][j]){
t|=(1<<j);
}
}
res&=t;
}
}
cout<<res;
return 0;
}