[NOIP模拟20]题解

来自达哥的问候……

A.周

究级难题,完全不可做QAQ

#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;
}
View Code

B.任

题目中特意强调了简单路径,往无环图的性质上想。显然无环图联通块个数=点数-边数,那么直接二维前缀和维护黑块个数、横向边数、纵向边数即可。

#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;
}
View Code

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。

如果到了新一段等差数列的开始,就直接用树状数组计算逆序对数。另外,对于刚开始不完整的一段等差数列需要加特判。

#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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Rorschach-XR/p/11370466.html