[NOIP模拟20]题解
来自达哥的问候……
A.周
究级难题,完全不可做QAQ
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; int n; ll a[25],b[25],c[25],d[25],ans; void dfs(int step,ll mdx,ll lhb) { if(step>n) { ans=max(ans,mdx*lhb); return ; } //cout<<mdx<<' '<<lhb<<endl; dfs(step+1,mdx+a[step]>0?mdx+a[step]:0,lhb-b[step]>0?lhb-b[step]:0); dfs(step+1,mdx-d[step]>0?mdx-d[step]:0,lhb+c[step]>0?lhb+c[step]:0); return ; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&a[i],&b[i],&c[i],&d[i]); dfs(1,0,0); cout<<ans<<endl; return 0; }
B.任
题目中特意强调了简单路径,往无环图的性质上想。显然无环图联通块个数=点数-边数,那么直接二维前缀和维护黑块个数、横向边数、纵向边数即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=2019; int n,m,Q; int a[N][N]; int hl[N][N],sl[N][N],sum[N][N]; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } /* int getsum(int tmp[][N],int i,int j) { return tmp[i][j-1]+tmp[i-1][j]-tmp[i-1][j-1]; } */ int main() { /*freopen("dat.in","r",stdin); freopen("my.out","w",stdout);*/ n=read();m=read();Q=read(); char s[N]; for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) a[i][j]=s[j]-'0'; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j]; hl[i][j]=hl[i][j-1]+hl[i-1][j]-hl[i-1][j-1]; sl[i][j]=sl[i][j-1]+sl[i-1][j]-sl[i-1][j-1]; if(a[i-1][j]&&a[i][j])sl[i][j]++; if(a[i][j-1]&&a[i][j])hl[i][j]++; } for(int i=1;i<=Q;i++) { int x=read(),y=read(),xx=read(),yy=read(); int ans=sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1]; int cover1=hl[xx][yy]-hl[xx][y]-hl[x-1][yy]+hl[x-1][y]; int cover2=sl[xx][yy]-sl[x][yy]-sl[xx][y-1]+sl[x][y-1]; ans-=(cover1+cover2); printf("%d\n",ans); } return 0; }
C.飞
鬼畜值的计算公式其实就是$C_n^2$,所以每一对相交线贡献就是1,不用考虑多条线交于一点的情况。
其实手玩一下的话很容易发现题目要求的就是逆序对个数(只要你别像我一样以为这是一道美妙的数学题考场推2页A4纸公式就行)
但是达哥为了不让自己出的题被AK(虽说我们那场还是有AK的),把内存卡到了32M
肯定要在$x[]$的生成方式上寻求突破,可以发现$x[]$构成的序列由多个等差数列构成。如果$x[i]$和$x[i-1]$在同一段等差数列内,且$x[i-1]$和前面的数列构成了m个逆序对,那么$x[i]$一定可以和前面的数构成m-k个逆序对。因为每段中必然有一个数能和$x[i-1]$构成逆序对而不能和$x[i]$构成,所以每段贡献都要少1。
如果到了新一段等差数列的开始,就直接用树状数组计算逆序对数。另外,对于刚开始不完整的一段等差数列需要加特判。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<iostream> #include<cstring> using namespace std; #define int long long int n,ini,a,mod,maxx,ans; int c[100005]; int lb(int x){return x&-x;} int add(int x,int val) { for( ;x<=a;x+=lb(x)) c[x]+=val; } int sum(int x) { int res=0; for( ;x;x-=lb(x)) res+=c[x]; return res; } signed main() { scanf("%lld%lld%lld%lld",&n,&ini,&a,&mod); int old=ini; if(ini<a)add(ini+1,1); int now=0,num=0; for(int i=2;i<=n;i++) { ini=(ini+a)%mod; if(ini<a)num=i-sum(ini+1)-1,now++,add(ini+1,1); else num-=now; if(ini>=a&&ini<old)num++; ans+=num; } cout<<ans<<endl; return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。