liu_runda出的题再次$\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%$
周
题解
不可做~
任
题解
题目中为什么反复强调简单路径,没有环
没有环的图中点数-边数=联通块数
前缀和维护边的前缀和,和点的前缀和,
在维护边的前缀和不好维护转化为横着边前缀和,竖着边前缀和
注意边的边界问题
看边如何维护
就拿我的举例
你在当前为边且当前左面为边时置为1
那么当你统计答案时
ll bia=bianheng[x2][y2]-bianheng[x1-1][y2]-bianheng[x2][y1]+bianheng[x1-1][y1];
思考我们统计答案时要把x2相连的边切断
类似的,我们维护竖着的边时也要类似操作
代码
#include<bits/stdc++.h> using namespace std; #define ll int #define A 2101 char s[A][A]; ll vis[A][A],stax[4200000],stay[4200000],dian[A][A],bianheng[A][A],bianshu[A][A]; ll cnt=0,n,m,q; const ll nowx[5]={0,0,0,1,-1}; const ll nowy[5]={0,1,-1,0,0}; void dfs(ll x,ll y,ll x1,ll y1,ll x2,ll y2){ vis[x][y]=cnt; // printf("x=%lld y=%lld vis=%lld\n",x,y,vis[x][y]); for(ll i=1;i<=4;i++){ ll xnow=x+nowx[i],ynow=y+nowy[i]; if(xnow>x2||xnow<x1) continue; if(ynow>y2||ynow<y1) continue; if(s[xnow][ynow]-'0'==0) continue; if(vis[xnow][ynow]) continue; dfs(xnow,ynow,x1,y1,x2,y2); } } void bfs(ll x1,ll y1,ll x2,ll y2){ cnt=0; for(ll i=x1;i<=x2;i++) for(ll j=y1;j<=y2;j++){ if(s[i][j]=='1'&&!vis[i][j]){ ++cnt; dfs(i,j,x1,y1,x2,y2); } } for(ll i=x1;i<=x2;i++) for(ll j=y1;j<=y2;j++){ vis[i][j]=0; } printf("%d\n",cnt); } int main(){ scanf("%d%d%d",&n,&m,&q); for(ll i=1;i<=n;i++){ scanf("%s",s[i]+1); } // memset() for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++){ if(s[i][j]=='1'&&s[i][j-1]=='1'){ bianheng[i][j]++; } if(s[i][j]=='1'&&s[i-1][j]=='1'){ bianshu[i][j]++; } if(s[i][j]=='1'){ dian[i][j]++; } } for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++){ bianheng[i][j]=bianheng[i][j]+bianheng[i-1][j]+bianheng[i][j-1]-bianheng[i-1][j-1]; bianshu[i][j]=bianshu[i][j]+bianshu[i-1][j]+bianshu[i][j-1]-bianshu[i-1][j-1]; dian[i][j]=dian[i][j]+dian[i-1][j]+dian[i][j-1]-dian[i-1][j-1]; } // for(ll i=1;i<=n;i++,puts("")) // for(ll j=1;j<=m;j++){ // printf("bian=%d ",bianheng[i][j]); // } for(ll i=1,x1,x2,y1,y2;i<=q;i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); ll bia=bianheng[x2][y2]-bianheng[x1-1][y2]-bianheng[x2][y1]+bianheng[x1-1][y1]; // printf("%d %d %d %d bia=%d\n",bianheng[x2][y2],bianheng[x1-1][y2],bianheng[x2][y1],bianheng[x1-1][y1-1],bia); bia+=bianshu[x2][y2]-bianshu[x1][y2]-bianshu[x2][y1-1]+bianshu[x1][y1-1]; // printf("%d %d %d %d bia=%d\n",bianshu[x2][y2],bianshu[x1][y2],bianshu[x2][y1-1],bianshu[x1-1][y1-1],bia); ll dia=dian[x2][y2]-dian[x1-1][y2]-dian[x2][y1-1]+dian[x1-1][y1-1]; printf("%d\n",dia-bia); } }
飞
题解
其实就是求逆序对,然而逆序对是$n*log$的并不能过,思考优化
我们发现其实它给了多段等差数列,
先看所有等差数列都是完整的情况
我们在同一段等差数列上可以由$x[i-1]$推到$x[i]$思考我们找的是比当前大的个数,$x[i]=x[i-1]+a$所有之前等差数列贡献都要减一
那么设之前贡献$tmp$得到当前贡献$tmp-cnt(等差数列个数)$
那么我们思考$<a$(即等差数列首项)怎么维护,我们拿一个树状数组维护$<a$的所有值
假设当前值为$x$那么逆序对数就是$i-sum(x)-1$
然后我们考虑第一段不是完整的
假设当前$x$转移到$x2$中$x,x2$都比$begin$小那么等差数列上所有小于$begin$贡献都要减一(头一段上没有处于$x$--$x2$数不能转移)
若当前已经比$begin$大了把它当成一个正常等差数列
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 111111 ll c[A]; ll tus,n,a,mod,now,cnt=0,ans=0,last,tmp; void add(ll x,ll u){ for(ll i=x;i<=a;i+=i&-i) c[i]+=u; } ll sum(ll x){ ll ans=0; for(ll i=x;i>=1;i-=i&-i) ans+=c[i]; return ans; } int main(){ scanf("%lld%lld%lld%lld",&n,&tus,&a,&mod); ll now=tus; if(tus<=a){ add(tus+1,1); } for(ll i=2;i<=n;i++){ now=(a+now)%mod; if(now<a){ tmp=i-sum(now+1)-1; cnt++; add(now+1,1); } else{ tmp-=cnt; if(now<tus) tmp++; } ans+=tmp; } printf("%lld\n",ans); }