酣畅淋漓的一波 A K AK AK……
累死我了,第二天 7 : 00 7:00 7:00才醒。
直接模拟即可。
使用一个计数器 c n t cnt cnt,如果当前的 a i = b i a_i=b_i ai=bi,那么 c n t cnt cnt的值加一;否则归 0 0 0。若某个时刻 c n t cnt cnt的值不小于 3 3 3,说明满足要求,否则不满足要求。
考虑如果我们已经得到了 C C C,那么满足要求的 ( A , B ) (A,B) (A,B)对数就是 N − C N-C N−C的约数个数。
所以,我们预处理出每个数的约数个数。即,我们枚举 i ( 1 ≤ i ≤ n ) i(1≤i≤n) i(1≤i≤n),对于所有是 i i i倍数的数均加 1 1 1,从而在 O ( n ln n ) O(n \ln n) O(nlnn)的代价下求出了每个数的约数个数。
然后,枚举 C C C并累加满足要求的 ( A , B ) (A,B) (A,B)对即可。
时间复杂度 O ( n ln n ) O(n \ln n) O(nlnn)。
怎么有人跟我说是矩阵优化 d p dp dp呢
首先,如果每个区间的长度均为 1 1 1,我们该如何做呢?
这是一个显然的 d p dp dp,时间复杂度 O ( n k ) O(nk) O(nk)。
但是,每个区间的长度不一定是 1 1 1,如果我们把这些可能很长的区间拆成一个一个长度为 1 1 1的小块的话,时间复杂度原地爆炸成 O ( n 2 ) O(n^2) O(n2)。
观察 d p dp dp转移式,可以发现,在区间修改后我们的 d p dp dp式子在转移时需要区间求和。即,假设对于一个给定的区间 [ l , r ] [l,r] [l,r],目前走到了 i ( i > l ) i(i>l) i(i>l),那么它可以从区间 [ m a x ( i − r , 1 ) , i − l ] [max(i-r,1),i-l] [max(i−r,1),i−l]转移过来,显然这是一个区间求和。
所以我们想到了用前缀和优化。一边转移,一边维护前缀和,即可将时间复杂度优化到 O ( n k ) O(nk) O(nk)。
太套路了吧
通过观察,不难发现,这个序列是有周期的(纯循环或混循环),且周期的长度不会超过 m m m,周期之前的长度也不会超过 m m m。
所以,我们开桶,记录下这个值上一次出现在哪个位置。比如,对于 n = 1 0 10 , x = 2 , m = 7 n=10^{10}, x=2, m=7 n=1010,x=2,m=7的情况,序列是 2 , 4 , 2 , 4 … … 2,4,2,4…… 2,4,2,4……如果我们扫描到 2 2 2的时候,查桶时会发现 2 2 2已经出现过,此时循环节为 24 24 24。
有了循环节之后就很好做啦。
时间复杂度 O ( m ) O(m) O(m)。
怎么又考毒瘤的线段树啊
考虑维护两个序列 A , B A,B A,B:
①每一行的白格子最早出现在哪个位置。
②每一列的白格子最早出现在哪个位置。
考虑修改的时候,我们需要改变哪些值。
①修改了列( 1 1 1操作): 设 n o w now now为 B k B_k Bk。把序列 A A A中 1 1 1至 n o w − 1 now-1 now−1的数对 k k k取 m i n min min,然后 B k = 1 B_k=1 Bk=1;
②修改了行( 2 2 2操作): 设 n o w now now为 A k A_k Ak。把序列 B B B中 1 1 1至 n o w − 1 now-1 now−1的数对 k k k取 m i n min min,然后 A k = 1 A_k=1 Ak=1。
现在,我们把这题转换为了两种操作:
①区间对一个值取 m i n min min;
②单点查询。
显然,可以用两棵可爱的线段树维护。
但是,这里线段树维护的是区间的标记,并没有维护别的东西。即,假设我们要对 [ 3 , 7 ] [3,7] [3,7]这个区间对 4 4 4取 m i n min min,且 [ 3 , 7 ] [3,7] [3,7]分成了 [ 3 , 4 ] , [ 5 , 6 ] , [ 7 ] [3,4],[5,6],[7] [3,4],[5,6],[7]这三个区间,那么这三个区间的标记全部对 4 4 4取 m i n min min(初始时所有标记均为无穷大)。
那么单点查询呢?假设我们要查询某个序列第 k k k个位置的值。我们可以用根节点往下走,拐来拐去,记录下所经过的节点的 t a g tag tag的最小值 m i n v minv minv,并实时 p u s h d o w n pushdown pushdown。那么,最终查询的值就是 m i n ( m i n v , a k ) min(minv,a_k) min(minv,ak)。
所以,我们在 O ( n log n ) O(n\log n) O(nlogn)的时间复杂度内维护了这两个数组。
就在此时,你可能会发现,样例 3 3 3过不去了。
但是我们维护得都是对的呀?
答案是啥呢?
貌似这两个数组并不能得到最终的答案……
好吧,这题我也不会做
首先,我们回来想一想,我们认为
∑ i = 1 n m a x ( a i − 2 , 0 ) + ∑ i = 1 n m a x ( b i − 2 , 0 ) \sum_{i=1}^n max(a_i-2,0)+\sum_{i=1}^n max(b_i-2,0) i=1∑nmax(ai−2,0)+i=1∑nmax(bi−2,0)
就是答案。
但是,可以发现,如果一个黑格子的左边以及上面都没有白格子,那么它就会被算两遍。
我们现在要减去这个被重复算的值。如何计算呢?假设所有行修改的 x x x的最小值为 h m hm hm,所有列修改的 x x x的最小值 l m lm lm,那么这个值就是 m a x ( h m − 2 , 0 ) × m a x ( l m − 2 , 0 ) max(hm-2,0)×max(lm-2,0) max(hm−2,0)×max(lm−2,0)。
显然, h m , l m hm,lm hm,lm容易求出,最终减去这个值即可。
最后,注意:
①如果 n o w = 1 now=1 now=1,放弃修改,否则会出现死循环;
②最大值代表的 i n f inf inf开大;
③线段树开 8 8 8倍;
④别忘了答案的表达式中对 0 0 0取 m a x max max的那些部分。
总时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。
不知道自己在干什么,这题赛事竟然刚了 70 m i n 70min 70min才在距离比赛 18 m i n 18min 18min的时候刚出来。
线段树怎么还是不熟啊
A K AK AK之后貌似敲了一个巨佬的桌子,这里对他说对不起,影响了大家的做题体验。
另外,以后敢说几十个秋葵的人,我就把你变成秋葵。
#include
#define int long long
using namespace std;
string s;
signed main()
{
cin>>s;
int n=s.size()-1;
if (s[n]=='s') cout<<s<<"es"<<endl;
else cout<<s<<"s"<<endl;
return 0;
}
#include
#define int long long
using namespace std;
int n,len=0;
int a[1000005],b[1000005],c[1000005];
signed main()
{
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i]>>b[i];
for (int i=1;i<=n;i++)
{
if (a[i]==b[i])
{
len++;
if (len>=3) return cout<<"Yes"<<endl,0;
}
else len=0;
}
cout<<"No"<<endl;
return 0;
}
#include
#define int long long
using namespace std;
int n;
int cnt[1000005];
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
for (int j=i;j<=n;j+=i) cnt[j]++;
}
int ans=0;
for (int i=1;i<=n;i++) ans+=cnt[n-i];
cout<<ans<<endl;
return 0;
}
#include
#define int long long
using namespace std;
const int mod=998244353;
int n,k;
int l[1005],r[1005],dp[1000005],pre[2000005];
inline int query(int ll,int rr)
{
return ((pre[rr]-pre[ll-1])%mod+mod)%mod;
}
signed main()
{
cin>>n>>k;
for (int i=1;i<=k;i++) cin>>l[i]>>r[i];
dp[1]=1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=k;j++)
{
if (l[j]>=i) continue;
int nowr=min(r[j],i-1ll);
dp[i]=(dp[i]+max(query(i-nowr,i-l[j]),0ll))%mod;
}
pre[i]=(pre[i-1]+dp[i])%mod;
}
cout<<dp[n]%mod<<endl;
return 0;
}
#include
#define int long long
using namespace std;
int n,x,m,l,r;
int a[1000005],pre[1000005];
map<int,int> ma;
signed main()
{
cin>>n>>x>>m;
a[1]=x;
pre[1]=x;
ma[x]=1;
for (int i=2;i<=10*m;i++)
{
a[i]=(a[i-1]*a[i-1])%m;
if (ma[a[i]])
{
l=ma[a[i]];
r=i-1;
break;
}
ma[a[i]]=i;
pre[i]=pre[i-1]+a[i];
}
int ans=0,tim,lef,tot=0,tot2=0;
ans=pre[l-1];
tim=(n-(l-1))/(r-l+1);
lef=n-(l-1)-tim*(r-l+1);
for (int i=l;i<=l+lef-1;i++) tot+=a[i];
for (int i=l;i<=r;i++) tot2+=a[i];
cout<<ans+tot2*tim+tot<<endl;
return 0;
}
#include
#define int long long
#define inf 200000000000000007
using namespace std;
int n,q,opt,k,ans=0,mina,minb;
int a[2000005],b[2000005];
struct segmentree_a
{
int num;
int tag;
}treea[2000005];
struct segmentree_b
{
int num;
int tag;
}treeb[2000005];
inline void f_a(int rt,int l,int r,int k)
{
treea[rt].tag=min(treea[rt].tag,k);
}
inline void f_b(int rt,int l,int r,int k)
{
treeb[rt].tag=min(treeb[rt].tag,k);
}
inline void pushdown_a(int rt,int l,int r)
{
int mid=(l+r)>>1;
f_a(2*rt,l,mid,treea[rt].tag);
f_a(2*rt+1,mid+1,r,treea[rt].tag);
}
inline void pushdown_b(int rt,int l,int r)
{
int mid=(l+r)>>1;
f_b(2*rt,l,mid,treeb[rt].tag);
f_b(2*rt+1,mid+1,r,treeb[rt].tag);
}
inline void change_a(int nl,int nr,int l,int r,int rt,int k)
{
pushdown_a(rt,l,r);
if (nl<=l&&r<=nr)
{
treea[rt].tag=min(treea[rt].tag,k);
return;
}
int mid=(l+r)>>1;
if (nl<=mid) change_a(nl,nr,l,mid,2*rt,k);
if (nr>mid) change_a(nl,nr,mid+1,r,2*rt+1,k);
}
inline void change_b(int nl,int nr,int l,int r,int rt,int k)
{
pushdown_b(rt,l,r);
if (nl<=l&&r<=nr)
{
treeb[rt].tag=min(treeb[rt].tag,k);
return;
}
int mid=(l+r)>>1;
if (nl<=mid) change_b(nl,nr,l,mid,2*rt,k);
if (nr>mid) change_b(nl,nr,mid+1,r,2*rt+1,k);
}
inline int query_a(int nl,int l,int r,int rt)
{
pushdown_a(rt,l,r);
int mid=(l+r)>>1,ans;
if (l==r&&l==nl) return min(a[l],treea[rt].tag);
if (nl<=mid) ans=query_a(nl,l,mid,2*rt);
else ans=query_a(nl,mid+1,r,2*rt+1);
return ans;
}
inline int query_b(int nl,int l,int r,int rt)
{
pushdown_b(rt,l,r);
int mid=(l+r)>>1,ans;
if (l==r&&l==nl) return min(b[l],treeb[rt].tag);
if (nl<=mid) ans=query_b(nl,l,mid,2*rt);
else ans=query_b(nl,mid+1,r,2*rt+1);
return ans;
}
signed main()
{
cin>>n>>q;
mina=minb=n;
for (int i=1;i<=n;i++) a[i]=n;
for (int i=1;i<=n;i++) b[i]=n;
a[n]=b[n]=1;
for (int i=1;i<=8*n;i++) treea[i].tag=treeb[i].tag=inf;
while (q--)
{
cin>>opt>>k;
if (opt==1)
{
mina=min(mina,k);
int now=query_b(k,1,n,1);
if (now>1) change_a(1,now-1,1,n,1,k);
b[k]=1;
}
else
{
minb=min(minb,k);
int now=query_a(k,1,n,1);
if (now>1) change_b(1,now-1,1,n,1,k);
a[k]=1;
}
}
for (int i=1;i<=n;i++) a[i]=query_a(i,1,n,1);
for (int i=1;i<=n;i++) b[i]=query_b(i,1,n,1);
for (int i=2;i<=n-1;i++)
{
int cnt=max(a[i]-2ll,0ll);
ans+=cnt;
}
for (int i=2;i<=n-1;i++)
{
int cnt=max(b[i]-2ll,0ll);
ans+=cnt;
}
int another=1;
another*=max(0ll,mina-2);
another*=max(0ll,minb-2);
cout<<ans-another<<endl;
return 0;
}