zoukankan      html  css  js  c++  java
  • 洛谷八连测R5题解

      woc居然忘了早上有八连测T T 还好明早还有一场...今天的题除了T3都挺NOIP的...

      T1只需要按横坐标第一关键字,纵坐标第二关键字排序一个一个取就好了...

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio> 
    #include<algorithm>
    #define ll long long 
    using namespace std;
    const int maxn=500010,inf=1e9;
    struct poi{int x, y;}a[maxn];
    int T, n, m, k, ans;
    bool v1[maxn], v2[maxn];
    void read(int &k)
    {
        int f=1;k=0;char c=getchar();
        while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
        while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
        k*=f;
    }
    inline bool cmp(poi a, poi b){return a.x==b.x?a.y<b.y:a.x<b.x;}
    int main()
    {
        read(T);
        while(T--)
        {
            read(n); read(m); read(k); ans=0;
            for(int i=1;i<=k;i++) read(a[i].x), read(a[i].y), v1[a[i].x]=v2[a[i].y]=0;
            sort(a+1, a+1+k, cmp);
            for(int i=1;i<=k;i++)
                if((!v1[a[i].x])&&(!v2[a[i].y])) ans++, v1[a[i].x]=v2[a[i].y]=1;
            printf("%d
    ", ans);
        }
        return 0;
    }
    View Code

      T2是道好题,但是是经典题...怎么大原题出在八连测啊

      求回文子序列个数是个经典的区间DP,今天发现区间DP写记忆化会非常好写...(但是怎么感觉NOIP的时候可能会被卡常QAQ

      设f[l][r]为区间[l,r]的回文子序列个数,考虑暴力的做法再来优化. 因为数字的值域为[1,k<=30],所以可以枚举所有的数字来统计方案数.

      先预处理出pre[i][j],nxt[i][j]分别表示第i位前面最晚出现的j和第i为后面最早出现的j的位置,枚举数字来转移的时候,考虑一下,我们显然可以把这两个字符接在[nxt[l][x]+1,pre[r][x]-1]的所有回文子序列的两端,所以方案数加上f[nxt[l][x]+1,pre[r][x]-1],这个时候已经不存在x只出现一次和只出现两次的子序列了,所以如果nxt[l][x]<pre[r][x],我们可以加上x只出现两次的方案,如果nxt[l][x]<=pre[r][x],我们可以加上x只出现一次的方案,于是总的DP方程为(博客园的公式加载可能需要一点时间QAQ):

    $f[l][r]=sum_{x=1}^{k}f[nxt[l][x]+1][pre[r][x]-1]+[nxt[l][x]<pre[r][x]]+[nxt[l][x]leq pre[r][x]]$

    暴力代码:

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio> 
    #include<algorithm>
    #define ll long long 
    #define MOD(x) ((x)>=mod?(x)-mod:(x))
    using namespace std;
    const int maxn=6010,mod=1e9+7;
    int T, n, m, k, l, r;
    int a[maxn], f[maxn][maxn], pre[maxn][maxn], nxt[maxn][maxn], last[maxn];
    void read(int &k)
    {
        int f=1;k=0;char c=getchar();
        while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
        while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
        k*=f;
    }
    int dfs(int l, int r)
    {
        if(f[l][r]!=-1)return f[l][r];
        if(l>r)return 0;
        int ans=0;
        for(int i=1;i<=k;i++)
            if(nxt[l][i] && pre[r][i])
                ans+=dfs(nxt[l][i]+1, pre[r][i]-1)+(nxt[l][i]<=pre[r][i])+(nxt[l][i]<pre[r][i]), ans=MOD(MOD(ans));
        return f[l][r]=ans;
    }
    int main()
    {
        read(T);
        while(T--)
        {
            read(n); read(m); read(k); 
            memset(f, -1, sizeof(f));
            memset(last, 0, (k+1)<<2);
            for(int i=1;i<=n;i++) read(a[i]);
            for(int i=1;i<=n;i++) 
            {
                last[a[i]]=i;
                for(int j=1;j<=k;j++) pre[i][j]=last[j]; 
            }
            for(int i=1;i<=k;i++) last[i]=n+1;
            for(int i=n;i;i--) 
            {
                last[a[i]]=i;
                for(int j=1;j<=k;j++) nxt[i][j]=last[j];
            }
            for(int i=1;i<=m;i++) read(l), read(r), printf("%d
    ", dfs(l, r));
        }
        return 0;
    }
    View Code

      其实可以发现每移动一维,至多改变两种数字的贡献,所以我们可以从f[l+1][r]转移的时候去掉[l+1,r]中a[l]和a[l+1]的贡献,在[l,r]里重新加上a[l]和a[l+1]的贡献,注意如果a[l]==a[l+1]的话不能去掉两次加两次...

      DP方程不好写就不写了...

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio> 
    #include<algorithm>
    #define ll long long 
    #define MOD(x) ((x)>=mod?(x)-mod:(x))
    using namespace std;
    const int maxn=6010,mod=1e9+7;
    int T, n, m, k, l, r;
    int a[maxn], f[maxn][maxn], pre[maxn][maxn], nxt[maxn][maxn], last[maxn];
    void read(int &k)
    {
        int f=1;k=0;char c=getchar();
        while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
        while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
        k*=f;
    }
    int dfs(int l, int r);
    inline int calc(int l, int r, int x)
    {
        if(l>r || nxt[l][x]==n+1 || !pre[r][x] || !x) return 0;
        int ans=dfs(nxt[l][x]+1, pre[r][x]-1)+(nxt[l][x]<=pre[r][x])+(nxt[l][x]<pre[r][x]);
        return MOD(ans);
    }
    int dfs(int l, int r)
    {
        if(f[l][r]!=-1)return f[l][r];
        if(l>r)return 0;
        f[l][r]=dfs(l+1, r);
        if(l<r && a[l]!=a[l+1]) f[l][r]=f[l][r]+calc(l, r, a[l+1]); f[l][r]=MOD(f[l][r]);
        if(a[l]!=a[l+1]) f[l][r]=f[l][r]-calc(l+1, r, a[l])+mod;    f[l][r]=MOD(f[l][r]);
        f[l][r]=f[l][r]-calc(l+1, r, a[l+1])+mod;                     f[l][r]=MOD(f[l][r]);        
        f[l][r]=f[l][r]+calc(l, r, a[l]);                      return f[l][r]=MOD(f[l][r]);
    }
    int main()
    {
        read(T);
        while(T--)
        {
            read(n); read(m); read(k); 
            for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[i][j]=-1;
            memset(last, 0, (k+1)<<2);
            for(int i=1;i<=n;i++) read(a[i]);
            for(int i=1;i<=n;i++) 
            {
                last[a[i]]=i;
                for(int j=1;j<=k;j++) pre[i][j]=last[j]; 
            }
            for(int i=1;i<=k;i++) last[i]=n+1;
            for(int i=n;i;i--) 
            {
                last[a[i]]=i;
                for(int j=1;j<=k;j++) nxt[i][j]=last[j];
            }
            for(int i=1;i<=m;i++) read(l), read(r), printf("%d
    ", dfs(l, r));
        }
        return 0;
    }
    View Code

      T3是道思维题...

      有两种做法,一种是正解:即求出$sum_{i=1}^{n}i^2*(a_i-a_j)$和$sum_{i=1}^{n}i*(a_i-a_j)$,两式相除即可...这个可以在模意义下做就不用担心爆long long

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio> 
    #include<algorithm>
    #define ll long long 
    #define MOD(x) ((x)>=mod?(x)-mod:(x))
    using namespace std;
    const int maxn=500010,mod=1e9+7;
    unsigned int T, n, x, sum1, sum2;
    void read(unsigned int &k)
    {
        int f=1;k=0;char c=getchar();
        while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
        while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
        k*=f;
    }
    inline int power(int a, int b)
    {
        int ans=1;
        for(;b;b>>=1, a=1ll*a*a%mod)
        if(b&1) ans=1ll*ans*a%mod;
        return ans;
    }
    int main()
    {
        read(T);
        while(T--)
        {
            read(n); sum1=sum2=0;
            for(int i=1;i<=n;i++) read(x), sum1=(1ll*sum1+1ll*i*x)%mod, sum2=(1ll*sum2+1ll*i*i%mod*x)%mod;
            for(int i=1;i<=n;i++) read(x), sum1=1ll*sum1-1ll*i*x%mod+mod, sum2=(1ll*sum2-1ll*i*i%mod*x%mod+mod), sum1=MOD(sum1), sum2=MOD(sum2);
            if(!sum1) puts("0");
                else printf("1 %lld
    ", 1ll*sum2*power(sum1, mod-2)%mod);
        }
        return 0;
    }
    View Code

      第二种做法是异或.开一个数组,对于每个数,二进制下哪位是1就给数组的哪位的异或上当前位置,如果最后数组有哪一位不是0就输出就好了...

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio> 
    #include<algorithm>
    #define ll long long 
    using namespace std;
    const int maxn=500010,inf=1e9;
    unsigned int n, x, now, sum, T;
    int f[33];
    void read(unsigned int &k)
    {
        int f=1;k=0;char c=getchar();
        while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
        while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
        k*=f;
    }
    int main()
    {
        read(T);
        while(T--)
        {
            read(n); sum=0;
            memset(f, 0, sizeof(f));
            for(int i=1;i<=n;i++)
            {
                read(x); sum^=x; now=0;
                while(x)
                {
                    now++;
                    if(x&1) f[now]^=i;
                    x>>=1;
                }
            }
            for(int i=1;i<=n;i++)
            {
                read(x); sum^=x; now=0;
                while(x)
                {
                    now++;
                    if(x&1) f[now]^=i;
                    x>>=1;
                }
            }
            if(!sum) puts("0");
                else
                {
                    for(int i=1;i<=32;i++)
                    if(f[i])
                    {
                        printf("1 %d
    ", f[i]);
                        break;
                    }
                }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    python学习笔记二
    计网第三章
    python学习笔记一
    Java基础第二十七天总结——网络编程
    Java基础第二十六天总结——IO流
    Java基础第二十五天总结——泛型
    Java基础第二十四天总结——集合
    Java基础第二十三天总结——集合
    Java基础第二十二天总结——枚举类与注解
    Java基础第二十一天总结——日期时间API
  • 原文地址:https://www.cnblogs.com/Sakits/p/7748424.html
Copyright © 2011-2022 走看看