缇翾唐闸
ABC315 Ex
就当半在线卷积学习笔记了🙃
首先如果已经求得\(F[l,mid],G[l,mid]\)
这里先假设\(f_0\)有值吧
如果我们直接让\(F\)和\(G[0,r-l+1)\),这样会出现\(r-l+1>mid\)的情况
不过可以发现,如果长度为\(2\)的次幂,这样的情况只会在\(l=0\)的情况出现
然后分桃
\(l=0\),我们让\(F(0,mid),G(0,mid)\)卷起来,这样只会出现\(F(0,mid)\)卷\(G[mid,r)\)的情况没考虑到
\(l\not=0\),正常的是\(F[l,mid)\)卷\(G[0,r-l+1)\),由于前面\(l=0\)的情况有一部分没算到,我们在加上\(G[l,mid)\)卷\(F[0,r-l+1)\)
对于这个题,我们只用求\(g_i=\sum\limits_{j=0}^{i-1}f_j\),同样这里分治一下
注意这个前缀和是\(i-1\)
Show Code
#include<bits/stdc++.h>
#define eps 1e-5
using namespace std;
const int MAXN=1e6+5;
const int MOD=998244353;
const int g=3;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int Rev[MAXN*4];
struct Poly{
vector<int>U;
void NTT(int Limit,int type)
{
int Len=(1<<Limit);
for(int i=0;i<Len;i++)
{
Rev[i]=((Rev[i>>1]>>1)|((i&1)<<(Limit-1)));
}
while(U.size()<Len)
{
U.push_back(0);
}
for(int i=0;i<Len;i++)
{
if(i<Rev[i])
{
swap(U[i],U[Rev[i]]);
}
}
for(int l=1;l<Len;l<<=1)
{
int Wn=Pow(g,(MOD-1)/(l<<1),MOD);
if(type==-1)
{
Wn=inv(Wn,MOD);
}
for(int i=0;i<Len;i+=(l<<1))
{
int W=1;
for(int j=i;j<i+l;j++,W=((long long)W*Wn)%MOD)
{
int Xc=U[j];
int Yc=((long long)U[j+l]*W)%MOD;
U[j]=((long long)Xc+Yc)%MOD;
U[j+l]=((long long)Xc-Yc+MOD)%MOD;
}
}
}
if(type==-1)
{
int Liv=inv(Len,MOD);
for(int i=0;i<Len;i++)
{
U[i]=((long long)U[i]*Liv)%MOD;
}
}
}
}A,B;
Poly Mul_NTT(Poly A,Poly B)
{
int N=A.U.size();
int M=B.U.size();
int nox=1;
int Lm=0;
while(nox<=(N+M-2))
{
nox<<=1;
Lm++;
}
A.NTT(Lm,1);
B.NTT(Lm,1);
for(int i=0;i<nox;i++)
{
A.U[i]=((long long)A.U[i]*B.U[i])%MOD;
}
A.NTT(Lm,-1);
while(A.U.size()>(N+M-1))
{
A.U.pop_back();
}
return A;
}
int n;
int G[MAXN];
int F[MAXN];
int a[MAXN];
void solve(int l,int r)
{
if(l+1==r)
{
if(l>1)
{
G[l]=((long long)G[l-1]+G[l])%MOD;
F[l]=((long long)G[l]*a[l])%MOD;
}
return;
}
int mid=(l+r)>>1;
solve(l,mid);
if(l==0)
{
A.U.resize(mid+1,0);
for(int i=0;i<mid;i++)
{
A.U[i]=F[i];
}
A=Mul_NTT(A,A);
for(int i=mid;i<r;i++)
{
G[i]=((long long)G[i]+A.U[i])%MOD;
}
}
else
{
Poly A,B;
A.U.clear();
B.U.clear();
for(int i=l;i<r;i++)
{
A.U.push_back(F[i-l]);
if(i<mid)
{
B.U.push_back(F[i]);
}
}
A=Mul_NTT(A,B);
for(int i=mid;i<r;i++)
{
G[i]=((long long)G[i]+2ll*A.U[i-l])%MOD;
}
}
solve(mid,r);
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n+1;i++)
{
scanf("%d",&a[i]);
}
int N=n;
int Lit=1;
int Nox=0;
while(Lit<=n+1)
{
Lit=(Lit<<1);
Nox++;
}
n=Lit;
F[1]=1;
solve(0,n);
for(int i=2;i<=N+1;i++)
{
printf("%d ",F[i]);
}
}
CF848D Shake It!
甚至连第一步都没想到
先把最小割转化成最大流
考虑第一次操作产生点\(u\),路径为\(S\rightarrow u\rightarrow T\)
如果后面的操作没有选到\((S,T)\),那最大流就是\(S\rightarrow u,u\rightarrow T\)的两个子问题最大流中小的那个\(+1\),就相当于是串联一下
考虑我们\(S\rightarrow T\)的最大流实际上就是由若干个\(u\)并联组合起来
这个并联和串联一起计数好像不好弄
考虑\(f_{i,j}\)表示\(i\)次操作,最大流为\(j\)时\(S\rightarrow T\)的方案数
\(g_{i,j}\)表示\(i\)次操作,最大流为\(j\)时\(S\rightarrow u\rightarrow T\)的方案数且后面不能操作\((S,T)\)的方案数
\(f\rightarrow g\)的转移不难写出,\(g_{i,j}=\sum\limits_{a+b=i-1}\sum\limits_{min(p,q)=j}f_{a,p}f_{b,q}\)
这玩意用个后缀优化一下
\(g\rightarrow f\)是个完全背包,就是把\((i,j,g_{i,j})\)看成一个物品,不过这里相同物品转移的时候注意要去重,因为两个物品合并时两个相同的方案之间应该没有顺序,这里我们直接枚举选了\(k\)次,这\(k\)次要分到\(g_{i,j}\)中的一个
这里把\(i\)作为阶段,每次可以求出\(g_{i,j}\)并直接拿去算背包
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244853;
const int MAXN=105;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int n,m;
int f[MAXN][MAXN];
int g[MAXN][MAXN];
int Sf[MAXN][MAXN];
int Sg[MAXN][MAXN];
int h[MAXN][MAXN];
int Inv[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
Inv[i]=inv(i,MOD);
}
f[0][1]=1;
h[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=n+1;j>=1;j--)
{
Sf[i-1][j]=((long long)Sf[i-1][j+1]+f[i-1][j])%MOD;
}
for(int j=n+1;j>=1;j--)
{
for(int k=0;k<=i-1;k++)
{
Sg[i][j]=((long long)Sg[i][j]+((long long)Sf[i-1-k][j]*Sf[k][j])%MOD)%MOD;
}
g[i][j]=((long long)Sg[i][j]-Sg[i][j+1]+MOD)%MOD;
}
for(int j=1;j<=n+1;j++)
{
for(int x=n;x>=0;x--)
{
for(int y=n+1;y>=0;y--)
{
int now=g[i][j];
for(int k=1;x-i*k>=0&&y-(j*k)>=0;k++)
{
h[x][y]=(((long long)h[x-i*k][y-(j*k)]*now)%MOD+h[x][y])%MOD;
now=((long long)now*Inv[k+1])%MOD;
now=((long long)now*(g[i][j]+k))%MOD;
}
}
}
}
for(int j=1;j<=n+1;j++)
{
f[i][j]=h[i][j-1];
}
}
printf("%d\n",f[n][m+1]);
}
ABC310Ex Negative Cost
\(nb\)性质题
翻译的官方题解???
设\(L=300\)
首先存在我们选得招式的一种的前缀和不可能超过\(2L\),原因很简单,如果超过\(2L\)我们就选\(>0\)的肯定不劣
然后对于一个合法的方案可以有若干个合法方案拼接起来,存在一种划分方案使得每种方案选的个数\(\le2L\)
证明似乎是考虑找一个划分点为前面都是加魔力值后面是减,这样的点肯定是每\(2L\)个至少出现一次
因此我们可以用\(dp\)求出选\(i\)个招式能造成的最大伤害\(D_i\),然后考虑把他们拼起来
我们设\(D_i/i\)最大的\(i\)为\(K\)
可以证明除\(K\)以外每个\(i\)最多选\(2L\)次,大概是因为我们尽量是要凑出\(H\bmod D_K\),这个用其他数来凑,然后在用\(K\)填
然后再跑个完全背包即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=305;
int n;
long long H;
int c[MAXN];
long long d[MAXN];
long long f[MAXN*2][MAXN*2];
long long D[MAXN*2];
long long g[(MAXN*2)*(MAXN*2)];
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %lld",&n,&H);
int L=300;
for(int i=1;i<=n;i++)
{
scanf("%d %lld",&c[i],&d[i]);
}
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=0;i<2*L;i++)
{
for(int j=0;j<=2*L;j++)
{
for(int k=1;k<=n;k++)
{
int To=j-c[k];
if(To>=0)
{
f[i+1][To]=max(f[i+1][To],f[i][j]+d[k]);
}
}
}
}
double Maxi=0;
int Key;
for(int i=1;i<=2*L;i++)
{
D[i]=-0x3f3f3f3f3f3f3f3f;
for(int j=0;j<=2*L;j++)
{
D[i]=max(D[i],f[i][j]);
}
if((D[i]*1.0/i)>Maxi)
{
Maxi=(D[i]*1.0/i);
Key=i;
}
}
//printf("%d %lld??\n",Key,D[Key]);
memset(g,-0x3f,sizeof(g));
g[0]=0;
for(int i=1;i<=2*L;i++)
{
for(int j=i;j<=(2*L)*(2*L);j++)
{
g[j]=max(g[j],g[j-i]+D[i]);
}
}
long long Res=1e18;
for(int i=0;i<=(2*L)*(2*L);i++)
{
long long Rest=max(H-g[i],0ll);
long long Ned;
if(Rest%D[Key])
{
Ned=((Rest/D[Key])+1)*Key;
}
else
{
Ned=((Rest/D[Key]))*Key;
}
Res=min(Res,Ned+i);
}
printf("%lld\n",Res);
return 0;
}
CF735E
可能要成智障了/kk
一个不难想到得思路是设\(dp_{x,i,j}\)表示在\(x\)中,子树内最近染色点距离为\(i\),子树外最近为\(j\)的方案数
不过这样的话要考虑很多东西,而且细节颇多(被题解搏杀了/kk
考虑换一种思路,\(dp_{x,i,j}\)表示表示在\(x\)中,子树内最近染色点距离为\(i\)而子树内最远未被满足点的需求为\(j\)的方案数,这玩意转移起来可能就方便,可能就不用考虑\(k+1\)的点了
而实际上仔细想想就可以发现\(i,j\)无法共存,因为如果\(i+j>k\)的话如果\(i\)不染色就一定就不上了,所以只用一维就够了
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=105;
int n,k;
int x,y;
vector<int>g[MAXN];
int dp[MAXN][55];
int Tmp[55];
void dfs(int x,int f)
{
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
dfs(v,x);
}
dp[x][k+1]=dp[x][0]=1;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
for(int p=0;p<=2*k;p++)
{
Tmp[p]=dp[x][p];
dp[x][p]=0;
}
for(int p=0;p<=2*k;p++)
{
for(int q=0;q<=2*k;q++)
{
if(p+q<=2*k)
{
dp[x][min(p,q+1)]=((long long)dp[x][min(p,q+1)]+((long long)Tmp[p]*dp[v][q])%MOD)%MOD;
}
else
{
dp[x][max(p,q+1)]=((long long)dp[x][max(p,q+1)]+((long long)Tmp[p]*dp[v][q])%MOD)%MOD;
}
}
}
}
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0);
int Res=0;
for(int i=0;i<=k;i++)
{
Res=((long long)Res+dp[1][i])%MOD;
}
printf("%d\n",Res);
}
ABC219Ex
没网了只能写题解/kk
不难看出这玩意就是左右来回走,也不难想到区间\(dp\)
不过只用这些是无法统计答案的,也不可能加上时间这一维度
考虑对问题进行一些转化,如果我们能事先知道最后要选\(k\)个点,那么我们每走一步答案都会\(-1\)
于是乎可以加上还要选多少个这一维度,然后就可以转移了
Show Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=305;
struct node{
int x,len;
}a[MAXN];
bool operator<(node x,node y)
{
return x.x<y.x;
}
int n;
int dp[MAXN][MAXN][MAXN][2];
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld %lld",&a[i].x,&a[i].len);
}
a[++n]=(node){0,0};
sort(a+1,a+1+n);
int Key;
for(int i=1;i<=n;i++)
{
if(a[i].x==0&&a[i].len==0)
{
Key=i;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=0;k<=n;k++)
{
dp[i][j][k][0]=-1e18;
dp[i][j][k][1]=-1e18;
}
}
}
for(int i=0;i<=n;i++)
{
dp[Key][Key][i][0]=dp[Key][Key][i][1]=0;
}
for(int len=1;len<n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
for(int k=0;k<=n;k++)
{
if(dp[l][r][k][0]!=-0x3f3f3f3f)
{
if(l>=2)
{
int Ned=(k*(a[l].x-a[l-1].x));
dp[l-1][r][k][0]=max(dp[l-1][r][k][0],dp[l][r][k][0]-Ned);
if(k)
{
dp[l-1][r][k-1][0]=max(dp[l-1][r][k-1][0],dp[l][r][k][0]-Ned+a[l-1].len);
}
}
if(r<n)
{
int Ned=(k*(a[r+1].x-a[l].x));
dp[l][r+1][k][1]=max(dp[l][r+1][k][1],dp[l][r][k][0]-Ned);
if(k)
{
dp[l][r+1][k-1][1]=max(dp[l][r+1][k-1][1],dp[l][r][k][0]-Ned+a[r+1].len);
}
}
}
if(dp[l][r][k][1]!=-0x3f3f3f3f)
{
if(l>=2)
{
int Ned=(k*(a[r].x-a[l-1].x));
dp[l-1][r][k][0]=max(dp[l-1][r][k][0],dp[l][r][k][1]-Ned);
if(k)
{
dp[l-1][r][k-1][0]=max(dp[l-1][r][k-1][0],dp[l][r][k][1]-Ned+a[l-1].len);
}
}
if(r<n)
{
int Ned=(k*(a[r+1].x-a[r].x));
dp[l][r+1][k][1]=max(dp[l][r+1][k][1],dp[l][r][k][1]-Ned);
if(k)
{
dp[l][r+1][k-1][1]=max(dp[l][r+1][k-1][1],dp[l][r][k][1]-Ned+a[r+1].len);
}
}
}
}
}
}
printf("%lld\n",max(dp[1][n][0][0],dp[1][n][0][1]));
}
[HNOI2019] 校园旅行
喵喵题
首先30pts的做法,我们可以直接维护每个点对是否可行,用队列拓展即可
这个做法的瓶颈在于如果我们固定某个点对,那么相当于会遍历整张图,时间复杂度\(O(nm)\)
考虑删去一些不必要的边,我们发现从两端拓展时如果一直是相同颜色的话只和奇偶性有关,仔细一想不同色同样是这样
进一步考虑,不同色的情况可以发现就是个二分图,不管怎么走奇偶性不变,所以只用保留连通图的一个生成树
而同色的同样如果是二分图就只保留生成树,否则存在奇环,无所谓奇偶可以直接连个自环
这样搞完后边数就是\(O(n)\)的了
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5005;
int n,m,q;
int x,y;
char s[MAXN];
vector<int>g[MAXN];
vector<int>G[MAXN];
int fa[MAXN];
int find(int x)
{
if(fa[x]==x)
{
return x;
}
fa[x]=find(fa[x]);
return fa[x];
}
void unionn(int i,int j)
{
fa[find(i)]=find(j);
return;
}
int vis[MAXN];
int col[MAXN];
bool f=0;
void dfs1(int x,int c)
{
if(col[x])
{
if(col[x]==c)
{
}
else
{
f=1;
}
return;
}
col[x]=c;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(s[v]==s[x])
{
dfs1(v,(c==1)?2:1);
if(find(v)!=find(x))
{
G[x].push_back(v);
G[v].push_back(x);
unionn(x,v);
}
}
}
}
void dfs2(int x)
{
if(vis[x])
{
return;
}
vis[x]=1;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(s[v]!=s[x])
{
dfs2(v);
if(find(v)!=find(x))
{
G[x].push_back(v);
G[v].push_back(x);
unionn(x,v);
}
}
}
}
int dp[MAXN][MAXN];
queue<pair<int,int> >Q;
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&m,&q);
scanf("%s",s+1);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
for(int i=1;i<=n;i++)
{
if(!col[i])
{
f=0;
dfs1(i,1);
if(f)
{
G[i].push_back(i);
}
}
}
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
dfs2(i);
}
}
for(int i=1;i<=n;i++)
{
Q.push(make_pair(i,i));
for(int j=0;j<g[i].size();j++)
{
int v=g[i][j];
if(s[i]==s[v])
{
if(i<v)
{
Q.push(make_pair(i,v));
}
}
}
}
while(Q.size())
{
pair<int,int>Tmp=Q.front();
Q.pop();
if(dp[Tmp.first][Tmp.second])
{
continue;
}
dp[Tmp.first][Tmp.second]=1;
for(int i=0;i<G[Tmp.first].size();i++)
{
for(int j=0;j<G[Tmp.second].size();j++)
{
int u=G[Tmp.first][i];
int v=G[Tmp.second][j];
if(s[u]==s[v])
{
if(u>v)
{
swap(u,v);
}
Q.push(make_pair(u,v));
}
}
}
}
while(q--)
{
scanf("%d %d",&x,&y);
if(x>y)
{
swap(x,y);
}
if(dp[x][y])
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}
ABC274Ex
长见识了,竟然可以用矩阵表示\(Hash\)
如果是数字串\(Hash\),两个数字串相加出来\(Hash\)同样相加
我们发现原因在于分配律
考虑什么和\(\oplus\)有分配律
注意到\((a\oplus b)\&c=(a\&c)\oplus(b\&c)\)
我们可以用\(\&\)代替乘法
不过\(\&\)的次幂没啥意义,注意到\((\oplus,\&)\)可以广义矩乘,发现同样有结合律
于是我们可以随机一个\(M\times M\)的矩阵\(P\)作为进制直接\(Hash\)找到最长相同的位置
这里复杂度有亿点高,首先矩乘得优化一维,另外这里用倍增不能用二分
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=6e5+5;
mt19937 Niuzi(114514);
int n,q;
long long a[MAXN];
int l1,l2,l3,r1,r2,r3;
struct Martix{
int n,m;
long long val[64];
void clear()
{
memset(val,0,sizeof(val));
}
Martix operator*(const Martix x)const{
Martix Res;
Res.clear();
Res.n=n;
Res.m=x.m;
for(int i=0;i<n;i++)
{
for(int k=0;k<m;k++)
{
if((val[i]>>k)&1)
{
Res.val[i]^=x.val[k];
}
}
}
return Res;
}
}P;
Martix Po[21];
long long dp[MAXN][21];
long long Mul(long long x,Martix A)
{
long long Res=0;
for(int k=0;k<A.m;k++)
{
if((x>>k)&1)
{
Res^=A.val[k];
}
}
return Res;
}
int main()
{
// freopen("rnm.txt","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
P.n=P.m=64;
for(int i=0;i<63;i++)
{
P.val[i]=(((long long)Niuzi())%2147483648)*(((long long)Niuzi())%2147483648);
}
Po[0]=P;
for(int i=1;i<=20;i++)
{
Po[i]=Po[i-1]*Po[i-1];
}
for(int i=1;i<=n;i++)
{
dp[i][0]=a[i];
}
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
dp[i][j]=Mul(dp[i][j-1],Po[j-1])^dp[i+(1<<(j-1))][j-1];
}
}
int Tot=0;
while(q--)
{
scanf("%d %d %d %d %d %d",&l1,&r1,&l2,&r2,&l3,&r3);
++Tot;
int Len=min(r1-l1+1,r3-l3+1);
int n1=l1;
int n2=l2;
int n3=l3;
for(int i=20;i>=0;i--)
{
if(n1+(1<<i)-1<=r1&&n3+(1<<i)-1<=r3)
{
long long R1=dp[n1][i]^dp[n2][i];
long long R2=dp[n3][i];
if(R1==R2)
{
n1+=(1ll<<i);
n2+=(1ll<<i);
n3+=(1ll<<i);
}
}
}
if((n1<=r1&&n3<=r3&&((a[n1]^a[n2])<a[n3]))||(n1>r1&&r3-l3>r1-l1))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
}
ARC093F
长见识了
这题最好不要开头就直接状压\(m\)个人,很明显这里有不好做
我们可以发现第一个人的位置是无所谓的,假设放在\(0\)号位
考虑把完全二叉树建出来
然后你会发现对于\(0\)到根路径上的右儿子会和\(1\)比较,所以我们的目的就是不能让\(A\)中的元素成为这些右儿子子树内的最小值
设从\(0\)到根组成的集合依次\(g_i\)
这里直接对\(g\)状压,然后你发现这玩意可以容斥搞一下
我们发现这个过程可以考虑\(A_i\)是否填入一个\(g\)中,对于不填的情况不好搞,所以要容斥,同时为了保证填的单调性,我们从大到小枚举\(A_i\)
设\(dp_{i,S}\)表示\([i,m]\)中\(g\)中 \(S\)已被填,其他任意的方案数
由于限制的是前缀,我们可以得到\(A_i\)前面有多少个被填的,所以不难得到转移
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=1e5+5;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int fac[MAXN];
int inv_fac[MAXN];
int C(int n,int m)
{
if(n<m||m<0)
{
return 0;
}
if(n==m||m==0)
{
return 1;
}
return ((((long long)fac[n]*inv_fac[m])%MOD)*inv_fac[n-m])%MOD;
}
int n,m;
int A[MAXN];
int dp[17][MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
fac[0]=1;
for(int i=1;i<=MAXN-5;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
inv_fac[MAXN-5]=inv(fac[MAXN-5],MOD);
for(int i=MAXN-5-1;i>=0;i--)
{
inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
}
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&A[i]);
A[i]--;
}
reverse(A+1,A+1+m);
dp[0][0]=1;
for(int i=1;i<=m;i++)
{
for(int S=0;S<(1<<n);S++)
{
dp[i][S]=((long long)dp[i][S]+dp[i-1][S])%MOD;
for(int k=0;k<n;k++)
{
if((S>>k)&1)
{
}
else
{
int Tyk=((long long)dp[i-1][S]*C((1<<n)-1-A[i]-S,(1<<k)-1))%MOD;
Tyk=((long long)Tyk*fac[(1<<k)])%MOD;
dp[i][S|(1<<k)]=((long long)dp[i][S|(1<<k)]+Tyk)%MOD;
}
}
}
}
int Res=0;
for(int i=0;i<(1<<n);i++)
{
int Rp=dp[m][i];
Rp=((long long)Rp*fac[(1<<n)-1-i])%MOD;
if(__builtin_popcount(i)&1)
{
Res=((long long)Res-Rp+MOD)%MOD;
}
else
{
Res=((long long)Res+Rp)%MOD;
}
}
Res=((long long)Res*(1<<n))%MOD;
printf("%d\n",Res);
}
[ARC070E] NarrowRectangles
首先考虑\(O(n^2)dp\),设\(dp_{i,j}\)表示前\(i\)个线段其右端点在\(j\)的最小代价,令\(len_i=R_i-L_i\)
可以发现如果把\(dp\)抽象为函数,则\(Min_{k=j-len_i}^{j+len_{i-1}}dp_{i-1,k}\)显然在\(dp_{i-1}\)为凹函数的情况下同样为凹函数
而\(|R_i-j|\)同样为凹函数,则\(dp_i\)同样为凹函数
再模拟一下可以发现,\(dp_i\)的斜率为整数,且中间有连续的一段斜率为\(0\),设\(L,R\)一段斜率为\(0\)
可以发现斜率小于\(0\)左移\(len_{i-1}\),大于\(0\)右移\(len_i\),如果访问位置,可以用\(lazy\)标记
在考虑\(|R_i-j|\),反应到斜率就是\(<R_i\)的\(-1\),\(>R_i\)的\(+1\)(如果值域小一点其实可以用线段树)
然后考虑\(R_i\)的位置对函数的影响
\(R_i<L\),则原来斜率\(=0\)的会变为\(1\),而原来的\(-1\)便为0
如果我们维护每个\(<0\)斜率的右端点,\(>0\)斜率的左端点,那就可以每个斜率包括长度为\(0\)的都用堆维护
最后的答案我们如果知道斜率来算是可以直接求出来的
但如果维护每次斜率为\(0\)的答案,可以发现,每次取\(Min\),\(L,R\)至少有一个点斜率不变,就可以把答案的取值设为两个端点中的一个即可
JOI2016 Skyscraper/ZJOI2012波浪
两个基本上就是一道题,就是\(JOI\)的那个,\(ZJOI\)有点枸捌
对于这种问题,如果不能按位考虑,我们就可以考虑插入
至于怎么维护插入后的值,如果我们从小到大插,也不知道这个元素左右两边的信息
考虑维护连通块,这个类似于某道\(ABC\),我们可以发现当前未确定的就是联通块的两边
这里还要把边界考虑进去
如果直接算\(A_i\)的贡献,这里会出现负值,我们可以对其差分,每次插完后产生的贡献即为未确定一边的个数\(\times(A_i-A_{i-1})\)
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int MAXM=5005;
long double dp[2][MAXN][MAXM][3];
__float128 f[2][MAXN][MAXM][3];
int n,m,K;
int a[MAXN];
void out(__float128 ans){
if(ans + 1e-14 >= 1){cout << "1." << string(K,'0') << endl; return;}
cout << "0.";
ans *= 10;
for(int i = 1 ; i <= K ; ++i){
cout << (int)(ans + (K == i) * 0.5);
ans = (ans - (int)ans) * 10;
}
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&m,&K);
for(int i=1;i<=n;i++)
{
a[i]=i;
}
sort(a+1,a+1+n);
if(K<=20)
{
dp[0][0][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=i;j++)
{
for(int k=0;k<=m;k++)
{
for(int d=0;d<=2;d++)
{
dp[(i&1)][j][k][d]=0;
}
}
}
for(int j=0;j<i;j++)
{
for(int k=0;k<=m;k++)
{
for(int d=0;d<=2;d++)
{
if(dp[(i-1)&1][j][k][d])
{
int tk=(k+(a[i]-a[i-1])*(2*j-d));
if(tk<=m)
{
if(d<2)
{
dp[i&1][j+1][tk][d+1]=(dp[i&1][j+1][tk][d+1]+dp[(i-1)&1][j][k][d]*(2-d)*1.0/i);
dp[i&1][j][tk][d+1]=(dp[i&1][j][tk][d+1]+dp[(i-1)&1][j][k][d]*(2-d)*1.0/i);
}
dp[i&1][j+1][tk][d]=(dp[i&1][j+1][tk][d]+dp[(i-1)&1][j][k][d]*(j+1-d)*1.0/i);
if(j>=1)
{
dp[i&1][j][tk][d]=(dp[i&1][j][tk][d]+dp[(i-1)&1][j][k][d]*(2*j-d)*1.0/i);
}
if(j>=2)
{
dp[i&1][j-1][tk][d]=(dp[i&1][j-1][tk][d]+dp[(i-1)&1][j][k][d]*(j-1)*1.0/i);
}
}
}
}
}
}
}
long double Res=0;
for(int i=0;i<m;i++)
{
Res+=dp[n&1][1][i][2];
}
Res=1-Res;
string dkf;
dkf+="%.";
dkf+=to_string(K);
dkf+="LF";
char sta[105];
strcpy(sta,dkf.c_str());
printf(sta,Res);
return 0;
}
f[0][0][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=i;j++)
{
for(int k=0;k<=m;k++)
{
for(int d=0;d<=2;d++)
{
f[(i&1)][j][k][d]=0;
}
}
}
for(int j=0;j<i;j++)
{
for(int k=0;k<=m;k++)
{
for(int d=0;d<=2;d++)
{
if(f[(i-1)&1][j][k][d])
{
int tk=(k+(a[i]-a[i-1])*(2*j-d));
if(tk<=m)
{
if(d<2)
{
f[i&1][j+1][tk][d+1]=(f[i&1][j+1][tk][d+1]+f[(i-1)&1][j][k][d]*(2-d)*1.0/i);
f[i&1][j][tk][d+1]=(f[i&1][j][tk][d+1]+f[(i-1)&1][j][k][d]*(2-d)*1.0/i);
}
f[i&1][j+1][tk][d]=(f[i&1][j+1][tk][d]+f[(i-1)&1][j][k][d]*(j+1-d)*1.0/i);
if(j>=1)
{
f[i&1][j][tk][d]=(f[i&1][j][tk][d]+f[(i-1)&1][j][k][d]*(2*j-d)*1.0/i);
}
if(j>=2)
{
f[i&1][j-1][tk][d]=(f[i&1][j-1][tk][d]+f[(i-1)&1][j][k][d]*(j-1)*1.0/i);
}
}
}
}
}
}
}
__float128 Res=0;
for(int i=0;i<m;i++)
{
Res+=f[n&1][1][i][2];
}
Res=1-Res;
out(Res);
}
ARC102F
鞲菝结论
观察\(P_{i-1}<P_i<P_{i+1}\)交换后变成\(P_{i-1}>P_i>P_{i+1}\)
逆序对减少了\(3\)
而交换是同奇偶交换
因此每次操作会对逆序对\(-3\)同时奇偶逆序对中的一个\(-1\)
设\(c_0\)为逆序对数,\(c_1\)为奇数位置逆序对数,\(c_2\)为偶数
因此合法操作序列一定满足\(c_0=3(c_1+c_2)\)
充分性不大会
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
int n;
int a[MAXN];
int Bit[MAXN];
int lowbit(int x)
{
return x&(-x);
}
void update(int k,int x)
{
for(int i=k;i<=n;i+=lowbit(i))
{
Bit[i]+=x;
}
}
int Sum(int k)
{
int res=0;
for(int i=k;i>=1;i-=lowbit(i))
{
res+=Bit[i];
}
return res;
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
memset(Bit,0,sizeof(Bit));
int c0=0;
for(int i=n;i>=1;i--)
{
c0+=Sum(a[i]);
update(a[i],1);
}
memset(Bit,0,sizeof(Bit));
int c1=0;
for(int i=n;i>=1;i-=2)
{
c1+=Sum(a[i]);
update(a[i],1);
}
memset(Bit,0,sizeof(Bit));
int c2=0;
for(int i=n-1;i>=1;i-=2)
{
c2+=Sum(a[i]);
update(a[i],1);
}
// printf("%d %d %d??\n",c0,c1,c2);
if((c1+c2)*3==c0)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
CF1886F Diamond Theft
很有趣的题
不难发现对于\(t=1/2\),其实最多使用一次,答案也不超过\(2n+2\)
然后对于\(t=3\),我最开始以为是贪心填\(s\)大的,不过似乎很容易\(Hack\)
考虑枚举钻石\(1\)的时间\(d\),我们可以得到\(t=1\)的最早时间
不难发现答案是\(n+2\)加上\(t=3\)额外用的
不妨倒着做,钻石\(2\)时间为\(0\),这样我们就可以得到\(t=2\)时间区间\([1,s_i]\),\(t=1,[d+1,d+s_i]\)
对于\(t=3\),实际上它最多可以被拆分为两个区间,我们假定它先是\([1,s_i]\)
考虑如何判断合法,我们发现左端点只有\(1,d+1\)
这里我们就只需分开判定左端点为\(d+1\)的能否选满左端点为\(1\)的能否选满
因为\(\ge d+1\)的满足的可以往前挪
先判定第一个,不满足就拆分\(3\)区间,最后判定第二个即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=10005;
int n;
struct node{
int t,s;
}a[MAXN];
int C[MAXN];
int D[MAXN];
int T[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
if(n==0)
{
printf("2");
return 0;
}
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a[i].t,&a[i].s);
}
int Res=0x3f3f3f3f;
for(int d=1;d<=2*n;d++)
{
int Tot=0;
for(int i=1;i<=3*n;i++)
{
C[i]=0;
D[i]=0;
T[i]=0;
}
int Ns=0;
for(int i=1;i<=n;i++)
{
if(a[i].t==1)
{
C[d+a[i].s]++;
D[d+a[i].s]++;
}
else if(a[i].t==2)
{
D[a[i].s]++;
}
else
{
if(a[i].s>d)
{
C[a[i].s]++;
D[a[i].s]++;
T[a[i].s]++;
}
else
{
D[a[i].s]++;
D[a[i].s+d]++;
C[a[i].s+d]++;
Ns++;
}
}
}
D[d]++;
int S=0;
priority_queue<int>Q;
for(int r=d+1;r<=3*n;r++)
{
S+=C[r];
while(T[r]--)
{
Q.push(r);
}
while(r-d<S)
{
if(!Q.size())
{
Tot=0x3f3f3f3f;
break;
}
else
{
int v=Q.top();
Q.pop();
S--;
Ns++;
D[v+d]++;
C[v+d]++;
}
}
}
S=0;
for(int i=1;i<=3*n;i++)
{
S+=D[i];
if(S>i)
{
Tot=0x3f3f3f3f;
}
else
{
}
}
Tot=max(Ns+n+2,Tot);
// printf("%d %d %d::\n",d,Tot,Ns);
Res=min(Res,Tot);
}
if(Res>=0x3f3f3f3f)
{
printf("-1\n");
return 0;
}
printf("%d\n",Res);
}
CF1874E
暴力:\(dp_{i,j}\)表示\(i\)个数\(fun\)为\(j\)方案数
转移\(dp_{i,j}=\sum dp_{k,p}\times dp_{i-1-k,j-p-i}\)
第二维用\(OGF\)表示
则\(F_i(x)=\sum F_k(x)F_{i-k-1}(x)x^i\)
厉害得地方来了,直接求出\(n^2\)个点值表示,然后用这些拉插一下
\(n^4\)有点卡厂
是不是很多\(FFT\)都可以这样优化个\(log\)呀
CF1860F
考虑两个直线相对位置最多变一次
这里我们处理出所有交点,直接枚举交点的情况即可
括号序用线段树维护
有点卡精度
为啥没做,因为没做到这
CF1861F
不可能想到的题
枚举\(j\)作为最大,二分第二大的\(k\),考虑网络流建图,建出来是个二分图,左边\(4\),右边\(n\)
我们直接枚举\(4\)个点的断边情况,得到另外\(n\)的断边式子,然后预处理一下即可
话说这是怎么想到网络流的
原来某一部点数较少可以直接枚举呀/kk
CF1879F
卡厂题
存活轮数为\(\lceil\dfrac{a_i}{x}\rceil h_i\)
考虑每个\(x\)的贡献,对于\(\lceil\dfrac{a_i}{x}\rceil\)相同的几段维护最大次大即可
关键是卡厂,这个得用\(st\)表,得注意不能重复
CF1845E
一个朴素的\(dp\)
考虑前\(i\)个位置填了\(j\)个\(1\)最少用了\(k\)步的方案数,这里我们计算步数是\(1\rightarrow 0\)
直接这样做是\(n^3\)的(不过卡卡常能过
考虑以一个点作为分界点,把右边的\(1\)全部移到左边,可以发现步数\(k\)次最多移\(\sqrt k\)个\(1\),因为可以发现距离为\(1\)的最多\(1\)个,距离为\(2\)的最多\(2\)个...因此步数为\(k\)最多挪\(\sqrt k\)个
因此固定\(i,k\),对应的\(j\)只有\(2\sqrt k\)种取值,直接转即可
CF1849F
最开始想直接\(Tire\)树上贪心,不过有的情况要分裂到多个子树
不妨想一个暴力的思路,直接暴力连\((i,j,a_i\oplus a_j)\)的边
如果答案可以为\(x\),则权\(\le x\)的边构成的导出子图是个二分图
这里不用我们确定最小权值,我们发现图联通即可确定二分图,这里直接做最小异或生成树即可,就算答案不是树边,答案构成的环也是不可替代的
CF1874B
这个确实没思路
拆位,对于相同的\((m,a,b)\),怎么操作都相同
我们考虑记录对于\((m,a,b)\)限制为\((c,d)\)或空的最小步数,状态有\(5^8\)个
CF1874D
推一下式子,不难得到\(\sum\limits_{i=1}^n\dfrac{2s_i}{a_i}-n\)
这个直接\(dp\)是\(n^3\)的,不难发现\(a\)不减,枚举填的\(a\)直接调和级数即可
CF1876D
反正我想不到
第二个条件不难转换成相同值\(RG\)交替
第一个条件你发现就是总方案减相同的\(/2\)
对于相同的,我们给相邻两个值相同的看成一段区间,区间不能互相包含,有交则是一个联通块,联通块内随便填
CF1148H Holy Diver
对于区间\(mex\),我们有可以离线维护出所有\(mex\)段的方法
考虑\(f_l\)为\([l,r]\)的\(mex\),\(f_l\)明显单调不升
可以发现添加一个\(x\),造成的影响为\(mex=x\)的段\(l,r\)
我们考虑处理这些段,每次二分一个\(y\)使得\(y\)最后出现的位置\(pos<r\),则\((pos,r]\)的\(mex\)为\(y\),继续划分
可以发现是这样做每次会划分一些段出来,时间复杂度均摊是对的
然后考虑处理答案,我们将每个段的左端点作为\(x\)轴,右端点作为\(y\)轴,每次查询就找\(([l,r],[l,r])\)
我们把每个\(mex\)段拆成两个\(3-side\)的矩形类似于【
可以发现每个点的贡献类似于一个一次函数,主席树维护一下,用\(set\)存一下每个颜色的编号
空间复杂度应该是有问题的,不过似乎卡不满?,或者有哪位可以证明一下
Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=2e5+5;
int n;
int ql,qr,k,a;
long long las;
set<pair<pair<int,int>,pair<int,int> > >S;
struct Seg_node1{
int lc,rc;
int mni;
};
struct Seg1{
Seg_node1 Tree[MAXN*4];
int cnt_node;
int rt;
void push_up(int p)
{
Tree[p].mni=min(Tree[ls].mni,Tree[rs].mni);
}
void Build(int &p,int l,int r)
{
p=++cnt_node;
if(l==r)
{
Tree[p].mni=0;
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
push_up(p);
}
void Insert(int &p,int l,int r,int k,int x)
{
if(l==r)
{
Tree[p].mni=x;
return;
}
int mid=(l+r)>>1;
if(k<=mid)
{
Insert(ls,l,mid,k,x);
}
else
{
Insert(rs,mid+1,r,k,x);
}
push_up(p);
}
int Query(int p,int l,int r,int ql,int qr,int x)
{
if(ql>qr)
{
return -1;
}
if(!p)
{
return -1;
}
if(Tree[p].mni>=x)
{
return -1;
}
if(l==r)
{
return l;
}
int mid=(l+r)>>1;
int Res=-1;
if(ql<=mid)
{
Res=Query(ls,l,mid,ql,qr,x);
}
if(Res!=-1)
{
return Res;
}
if(qr>mid)
{
Res=Query(rs,mid+1,r,ql,qr,x);
}
return Res;
}
int Qx(int p,int l,int r,int k)
{
if(l==r)
{
return Tree[p].mni;
}
int mid=(l+r)>>1;
if(k<=mid)
{
return Qx(ls,l,mid,k);
}
else
{
return Qx(rs,mid+1,r,k);
}
}
}t;
struct Seg_node{
int lc,rc;
long long dat1;
long long dat2;
long long lazy1;
long long lazy2;
};
int Outpx=0;
struct Seg{
Seg_node Tree[MAXN*125];
int cnt_node;
int Update(int o,int l,int r,int ql,int qr,int x)
{
int p=++cnt_node;
Tree[p]=Tree[o];
if(cnt_node>(MAXN*125)-5)
{
printf("jsff\n");
exit(0);
}
int op=1;
if(x<0)
{
op=-1;
}
if(min(r,qr)>=max(l,ql))
{
Tree[p].dat1+=(min(r,qr)-max(l,ql)+1)*op;
Tree[p].dat2+=(long long)x*(min(r,qr)-max(l,ql)+1);
}
if(l>=ql&&r<=qr)
{
Tree[p].lazy1+=op;
Tree[p].lazy2+=x;
return p;
}
int mid=(l+r)>>1;
if(ql<=mid)
{
ls=Update(ls,l,mid,ql,qr,x);
}
if(qr>mid)
{
rs=Update(rs,mid+1,r,ql,qr,x);
}
return p;
}
long long Qry(int p,int l,int r,int ql,int qr,int x)
{
if(!p)
{
return 0;
}
if(l>=ql&&r<=qr)
{
return ((long long)x+1)*(Tree[p].dat1)-Tree[p].dat2;
}
long long Res=0;
if(min(r,qr)>=max(l,ql))
{
long long d1=(min(r,qr)-max(l,ql)+1)*Tree[p].lazy1;
long long d2=(long long)Tree[p].lazy2*(min(r,qr)-max(l,ql)+1);
Res+=((long long)x+1)*d1-d2;
}
int mid=(l+r)>>1;
if(ql<=mid)
{
Res+=Qry(ls,l,mid,ql,qr,x);
}
if(qr>mid)
{
Res+=Qry(rs,mid+1,r,ql,qr,x);
}
return Res;
}
}T;
map<int,int>Tix;
set<pair<pair<int,int>,int> >Rt;
int Rjx;
void Uplord(pair<pair<int,int>,pair<int,int> >Tmp,int Tim)
{
int a=Tmp.first.first;
// if(a==0)
// {
// printf("%d %d %d??\n",Tmp.second.first,Tmp.second.second,Tim);
// }
int l=Tmp.second.first;
int r=Tmp.second.second;
int id=Tim;
++Rjx;
if(id<0)
{
id=-id;
}
int Ix=Tix[a];
int Dex=T.cnt_node;
// cerr<<l<<" "<<r<<endl;
Tix[a]=T.Update(Ix,1,n,l,r,Tim);
if(Rt.lower_bound(make_pair(make_pair(a,id),0))!=Rt.end())
{
auto tsut=*Rt.lower_bound(make_pair(make_pair(a,id),0));
if((tsut).first==make_pair(a,id))
{
Rt.erase(tsut);
}
}
Rt.insert(make_pair(make_pair(a,id),Tix[a]));
// if(a==0)
// {
// Outpx=1;
// cerr<<Tim<<endl;
// cerr<<l<<" "<<r<<endl;
// cerr<<Tix[a]<<endl;
// cerr<<T.Qry(Tix[a],1,n,1,2,2)<<"gjjg"<<endl;
// Outpx=0;
// }
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
t.Build(t.rt,0,n+1);
for(int id=1;id<=n;id++)
{
scanf("%d %d %d %d",&a,&ql,&qr,&k);
//printf("%d %d %d %d %d??\n",a,las,id,a+las,n+1);
a=(a+las)%(n+1);
//printf("%d %d %d??\n",a,las,id);
ql=(ql+las)%(id)+1;
qr=(qr+las)%(id)+1;
if(ql>qr)
{
swap(ql,qr);
}
k=(k+las)%(n+1);
auto it=S.lower_bound(make_pair(make_pair(a,0),make_pair(0,0)));
t.Insert(t.rt,0,n+1,a,id);
int Tsuki=0;
if(a==0)
{
Tsuki++;
}
//cerr<<Tsuki<<endl;
vector<pair<pair<int,int>,pair<int,int> > >V;
V.push_back(make_pair(make_pair(Tsuki,id),make_pair(id,id)));
if((it!=S.end())&&((*it).first.first==a))
{
//cerr<<"fc"<<endl;
int l=(*it).second.first;
int r=(*it).second.second;
Uplord((*it),-id);
S.erase(it);
//cerr<<a<<" "<<l<<" "<<r<<" "<<id<<endl;
while(l<=r)
{
//cerr<<l<<" "<<r<<endl;
int Ra=t.Query(t.rt,0,n+1,a+1,n,r);
//cerr<<Ra<<endl;
int il=max(l-1,t.Qx(1,0,n,Ra));
if(V.back().first.first==Ra)
{
V.back().second.first=il+1;
}
else
{
V.push_back(make_pair(make_pair(Ra,id),make_pair(il+1,r)));
}
r=il;
}
}
for(int i=0;i<V.size();i++)
{
auto pt=S.lower_bound(make_pair(make_pair(V[i].first.first,0),make_pair(0,0)));
if((pt!=S.end())&&((*pt).first.first==V[i].first.first))
{
auto tmp=(*pt);
S.erase(pt);
//cerr<<"fc"<<endl;
// Uplord(tmp,-id);
S.insert(make_pair(V[i].first,make_pair(tmp.second.first,V[i].second.second)));
Uplord(V[i],id);
//Uplord(make_pair(V[i].first,make_pair(tmp.second.first,V[i].second.second)),id);
}
else
{
Uplord(V[i],id);
S.insert(V[i]);
}
}
// for(auto ft=S.begin();ft!=S.end();ft++)
// {
// auto tmp=(*ft);
// printf("%d %d %d\n",tmp.first.first,tmp.second.first,tmp.second.second);
// }
//cerr<<a<<endl;
//cerr<<S.size()<<endl;
long long Res=0;
auto zt=Rt.upper_bound(make_pair(make_pair(k,qr),1e9+1));
//cerr<<Rt.size()<<endl;
//cerr<<(*Rt.begin()).first.first<<endl;
// for(auto ft=Rt.begin();ft!=Rt.end();ft++)
// {
// auto tmp=(*ft);
// printf("%d %d %d\n",tmp.first.first,tmp.first.second,tmp.second);
// }
//printf("%d %d %d??\n",k,qr,0);
if(zt!=Rt.begin())
{
//cerr<<"ffi"<<endl;
zt--;
auto lpx=(*zt);
if(lpx.first.first==k)
{
//cerr<<"fow"<<endl;
int rxt=lpx.second;
Res+=T.Qry(rxt,1,n,ql,qr,qr);
}
}
//cerr<<Res<<endl;
zt=Rt.upper_bound(make_pair(make_pair(k,ql-1),1e9+1));
if(zt!=Rt.begin())
{
zt--;
auto lpx=(*zt);
if(lpx.first.first==k)
{
int rxt=lpx.second;
Res-=T.Qry(rxt,1,n,ql,qr,ql-1);
}
}
las=Res;
printf("%lld\n",Res);
}
//cerr<<Rjx<<endl;
}