C:显然每p2个数会有一个0循环,其中22 32 52 72的循环会在200个数中出现,找到p2循环的位置就可以知道首位在模p2意义下是多少,并且循环位置几乎是唯一的(对72不满足但可能的位置也很少)。于是这样枚举范围就直接从1e9变成了1e9/44100。然后考虑暴力求μ验证,求μ可以以O(n1/3/lnn)的时间完成,即对n1/3内的质数暴力check并除掉,剩下的直接判断是不是平方数即可。最后只要相信200个数匹配一会就break了就能过了。
#include
using namespace std;
#define ll long long
#define N 210
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int prime[N],a[N],p4=-1,p9=-1,p25=-1,p49=-1,cnt=0;
inline calc(int x)
{
for (int i=1;i<=cnt;i++)
{
if (x%prime[i]==0) x/=prime[i];
if (x%prime[i]==0) return 0;
}
if (x==1) return 1;
int u=sqrt(x);
if (u*u==x||(u+1)*(u+1)==x) return 0;
else return 1;
}
signed main()
{
for (int i=2;i<=1000;i++)
{
bool flag=0;
for (int j=2;j
I:考虑将增加A的贡献直接加入dp值。要算贡献需要知道之后攻击了多少次,攻击时间的和,于是设f[i][j][k]为前i次后期望之后攻击j次攻击时间和为k的最大伤害,转移显然。倒着dp和其本质相同。
#include
using namespace std;
#define ll long long
#define N 110
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a[N],b[N],c[N],s[N];
ll f[2][N][N*N];
signed main()
{
int T=read();
while (T--)
{
n=read();
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(),c[i]=read();
s[n]=n;
for (int i=n-1;i>=1;i--) s[i]=s[i+1]+i;
for (int i=0;i=i+1))
{
f[i&1^1][j-1][k-(i+1)]=max(f[i&1^1][j-1][k-(i+1)],f[i&1][j][k]+a[i+1]);
}
}
}
cout<
J:看做一个零和博弈,那个式子看成先手的收益,相反数看成后手的收益。考虑纳什均衡,则要尽量使后手选择各后缀的收益均相同(事实上若有某后缀包含另一后缀,相同是不可能实现的,但容易发现被包含的后缀不分配概率一定最优,实际上这个分析没有任何卵用)。考虑对后缀数组以height最小值分治,这样所有跨过该位置的两后缀lcp即为该height值。递归下去算出两侧的最优答案,容易发现合并时两侧保留原本的概率分配是最优的,因为两侧自身如何分配互相之间并不相干,那么只要给两侧分配概率使后手选择两侧的收益相同即可。
#include
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,rk[N<<1],tmp[N<<1],sa[N],sa2[N],cnt[N],h[N],v[N],f[N][20],LG2[N];
char s[N];
void make()
{
int m=26;
for (int i=1;i<=n*2;i++) rk[i]=tmp[i]=0;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[rk[i]=(s[i]-'a'+1)]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int p=0;
for (int i=n-k+1;i<=n;i++) sa2[++p]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[rk[i]]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
for (int i=1;i<=n*2;i++) tmp[i]=rk[i];
p=1,rk[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m==n) break;
}
for (int i=1;i<=n;i++)
{
h[i]=max(h[i-1]-1,0);
while (s[i+h[i]]==s[sa[rk[i]-1]+h[i]]) h[i]++;
}
for (int i=1;i<=n;i++) v[i]=h[sa[i]];
for (int i=1;i<=n;i++) f[i][0]=i;
for (int i=2;i<=n;i++)
{
LG2[i]=LG2[i-1];
if ((2<
K:考虑一个暴力dp,即设f[i][j]为以i为左端点要合成j时右端点至少为多少。转移显然有f[i][j]=min(f[f[i][j-1]+1][j-1],nxt[i][j]),其中nxt[i][j]为i位置之后出现的第一个j的位置。
显然f[i][j]随i增加单调不降。这样对于询问(l,r,k),只要二分出满足f[i][k]<=r的最大i,然后答案即为(i-l+1)*(r+1)-f[l..i][k],也即要知道f数组某一段的和。
考虑怎么不暴力地求f。观察转移,可以发现是一个类似于倍增的过程,虽然还有个取min的步骤,但仍然可以大胆猜想,对于所有j,f[i][j]构成的相同数连续段数量之和是O(nlogn)级别的。
这样维护f数组的连续段即可。具体地,离线处理,将询问按要合成的大小从小到大排序。更新dp数组时从前往后处理,记录f的前缀和,更新完合并相邻的相同段。
#include
using namespace std;
#define ll long long
#define N 200010
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,q,a[N],nxt[N],p[N],top;
ll ans[N];
struct data
{
int l,r,x,i;ll s;
bool operator <(const data&a) const
{
return xn) return n+1;
int l=1,r=top,ans;
while (l<=r)
{
int mid=l+r>>1;
if (stk[mid].l<=x) ans=mid,l=mid+1;
else r=mid-1;
}
return stk[ans].x;
}
ll getdps(int x)
{
if (x==0) return 0;
int l=1,r=top,ans=0;
while (l<=r)
{
int mid=l+r>>1;
if (stk[mid].r<=x) ans=mid,l=mid+1;
else r=mid-1;
}
return stk[ans].s+1ll*(x-stk[ans].r)*stk[ans+1].x;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("k.in","r",stdin);
freopen("k.out","w",stdout);
#endif
n=read(),m=read(),q=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=q;i++) b[i].l=read(),b[i].r=read(),b[i].x=read(),b[i].i=i;
sort(b+1,b+q+1);
for (int i=1;i<=m;i++) p[i]=n+1;
for (int i=n;i>=1;i--) nxt[i]=p[a[i]],p[a[i]]=i;
top=1;stk[1].l=1,stk[1].r=n,stk[1].x=n+1,stk[1].s=1ll*(n+1)*n;
int cur=0;
for (int i=1;i<=m;i++)
{
int x=p[i],top_tmp=0;
for (int j=1;j<=top;j++)
{
int y=getdp(stk[j].x+1),last=stk[j].l-1;
while (stk[j].r>=x)
{
top_tmp++;
tmp[top_tmp].l=last+1,tmp[top_tmp].r=x,tmp[top_tmp].x=x;
tmp[top_tmp].s=tmp[top_tmp-1].s+1ll*(x-last)*x;
last=x;x=nxt[x];
}
if (last>1;
if (stk[mid].x<=b[cur].r) k=stk[mid].r,l=mid+1;
else r=mid-1;
}
if (k>=b[cur].l) ans[b[cur].i]=1ll*(k-b[cur].l+1)*(b[cur].r+1)-(getdps(k)-getdps(b[cur].l-1));
}
//for (int j=1;j<=top;j++) cout<