码迷,mamicode.com
首页 > 其他好文 > 详细

【学长虐学弟欢乐赛】Round3

时间:2015-03-02 19:13:36      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:随便搞搞

第三套题
出题人:faebdc
金牌爷的题这么神竟然还有学长AK了QAQ
题目全都是CF上的 T1 D T2 E T3 D
神犇地址:faebdc学长的blog
试题下载地址
数据下载地址
话说题解的ppt用色果然还是faebdc神犇的常用配色= =
虚化色块喷涂拼接
T1 CF305D Olya ans Graph
原题可以自己去CF找
学长给的是翻译后题面
【问题描述】
Olya 有一张 n 个点、 m 条边的有向无权图, 所有点从 1 到 n 标号。 现在 Olya 想知道
有多少种添加有向边的方案(任意添加) ,使得该图满足下列条件:
1、 从点 i 出发,可以到达点 i+1, i+2, …,n。
2、 任意从 u 到 v 的有向边满足不等式: u<v。
3、 两点之间最多有一条边。
4、 对于一对点 i、 j(i<j) ,若 j-ik,那么从 i 到 j 的最短距离等于 j-i。
5、 对于一对点 i、 j(i<j) ,若 j-i>k,那么从 i 到 j 的最短距离等于 j-i 或 j-i-k。
我们认为两种添加边的方案不同,当且仅当存在至少一对点 i、 j(i<j) ,一张图中有
一条从 i 连向 j 的边,而另一张图中没有。
帮助 Olya! 由于要求的答案可能太大,将答案对 1000000007 取模后输出。
【输入格式】
第一行包含三个用空格隔开的整数, n,m,k。 接下来 m 行描述原图的有向边。第 i 行
包含一对用空格隔开的整数 ui,vi,表示第 i 条有向边从 ui 连向 vi。
【输出格式】
输出一个整数,即答案模 1000000007。
【样例输入】
7 8 2
1 2
2 3
3 4
3 6
4 5
4 7
5 6
6 7
【样例输出】
2
【数据规模和约定】
保证输入中任意一对点 ui,vi 之间最多有一条边。并且保证输入中给出的 ui 不下降。
如果有多条有向边从 ui 出发, 那么这些边将按照 vi 升序给出。 保证原图中任意从 u 到 v
的有向边满足不等式: u<v。
对于 30%的数据,保证 n,m4。
对于 60%的数据,保证 n,m103
对于 100%的数据, 2n106,0m105,1k106
题目简洁易懂.由题目我们不难看出该图中只可能有两种边
为i->i+1,i->i+k+1
剩下的就很好办了
开始乱搞!
这两天来第一个AC的题QwQ
感人至深
我的代码(注释里解释很全)

//AC code by CreationAugust沙茶
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define P 1000000007
#define MAXM 100010
#define MAXN 1000010
#define MAXINT 0x7fffffff
using namespace std;
int n,m,k;
int u[MAXM],v[MAXM];
int pre[MAXN]={1};
bool flag[MAXN];
int maxn=-MAXINT,minn=MAXINT;
long long ans=1;
long long num;
int check()//可执行性检测(随便起个名别在意QwQ)
{
    for (int i=1;i<=m;i++)
    {
        if (v[i]-u[i]>1)
        {
            if (v[i]-u[i]-1!=k) return 0;//读入的这些边都是不符合条件的
            //存在这些边的情况下不可能有解 ( ⊙ o ⊙ ) 
            maxn=max(maxn,u[i]);
            minn=min(minn,u[i]);
            flag[u[i]]=1;//记录合法的边起点,暂时叫他可执行点好了→v→ 
        }
    }
    for (int i=1;i<=m;i++)
    {
        if (v[i]-u[i]>1)
            if (maxn-u[i]>k) return 0;//一条路径上不可能
            //同时有两条i->i+k+1的边存在
            //这种情况也不合法 ╮(╯▽╰)╭ 
    }
    return 1;
}
int main() 
{
    freopen("olya.in","r",stdin);
    freopen("olya.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=m;i++)
        scanf("%d%d",&u[i],&v[i]);
    for (int i=1;i<=n;i++)
        pre[i]=(pre[i-1]*2)%P;
    //每个点(除了太靠后的不考虑)
    //都可以向外连两种边0-0 
    //所以先预处理出裸的方案数量好了www 
    if (!check()) //有可能输入不合法 
    {
        cout<<"0"<<endl;
        return 0;
    }
    //开始乱搞! 
    for (int i=1;i<=n;i++)
        if (flag[i]) num++;//把i打成u[i]调了半天QAQ     
    if (maxn==-MAXINT)//可执行点有可能不存在ヾ(0ω0) 
    {
        int x=min(k,n-k-1);
        for (int i=0;i<x;i++)
        {
            ans+=pre[i];
            ans%=P;
        }
        int temp=n-k-1-k;
        ans=(ans%P+(pre[k]*temp%P))%P;
    }
    else
    {
        int t2=min(n-k-1,minn+k);//n-k-1或者minn+k之后
        //就不能连i->i+k+1的边了QwQ 
        int t1=max(maxn-k,1); 
        for (int i=t2;i>=t1;i--)
        {
            if (flag[i])
                num--;
            else
            {
                int temp=min(min(i-1,k-maxn+i),k);
                ans=(ans%P+pre[temp-num])%P;//除掉他们剩下的都是可用的方案 
            }
        }
    }
    cout<<ans<<endl;
}
这是标程
//std by faebdc神犇 果然强大竟然短那么多QAQ
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1050000;
const int MOD = 1000000007;
int n,m,k;
int cc,minz,maxz;
long long ans=0;
int erci[N];
int main()
{
    freopen("olya.in","r",stdin);
    freopen("olya.out","w",stdout);
    int i,j;
    int a,b;
    scanf("%d%d%d",&n,&m,&k);
    erci[0]=1;
    for(i=1;i<=n;i++)
        erci[i]=(erci[i-1]<<1)%MOD;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        if(a+1==b)
            continue;
        if(b-a!=k+1)
        {
            printf("0\n");
            return 0;
        }
        if(!cc)
            minz=a;
        cc++;
        maxz=a;
    }
    n-=k+1;
    if(n<=0)
    {
        printf("1\n");
        return 0;
    }
    if(!cc)
    {
        if(n<=k+1)
            ans=erci[n];
        else
        {
            ans=erci[k];
            ans*=n-k+1;
            ans%=MOD;
        }
        printf("%d\n",ans);
        return 0;
    }
    if(minz+k<maxz)
    {
        printf("0\n");
        return 0;
    }
    for(i=max(1,maxz-k);i<minz;i++)
        ans+=erci[min(k-cc,n-i-cc)];
    ans+=erci[min(k+1-cc,n-minz+1-cc)];
    ans%=MOD;
    printf("%d\n",ans);
    return 0;
}

T2 CF249E Endless Matrix
【问题描述】
有一个矩阵,里面有连续的数字,从 1 开始。若 a_{i,j}<a_{t,k} (i,j,t,k1) ,当
且仅当满足以下条件之一:
1. max(i,j)<max(t,k)
2. max(i,j)=max(t,k) 且 j<k
3. max(i,j)=max(t,k) , j=k 且 i>t
例如前 36 个数字形成的矩阵:
1 2 5 10 17 26
4 3 6 11 18 27
9 8 7 12 19 28
16 15 14 13 20 29
25 24 23 22 21 30
36 35 34 33 32 31
对于给定的 x_1,y_1,x_2 和 y_2 (x_1x_2,y_1y_2) ,求
x_2i=x_1y_2j=y_1 ai,j
【输入格式】
第一行一个正整数 t,表示数据组数。
接下来 t 行每行四个正整数 x_1,y_1,x_2 和 y_2。
【输出格式】
对每一个询问输出答案。 如果答案超过 10 位, 输出三个”. “, 然后输出答案的后十位。
否则直接输出答案。
【样例输入】
51
1 1 1
2 2 3 3
2 3 5 6
100 87 288 2002
4 2 5 4
【样例输出】
1
24
300
…5679392764
111
【数据规模和约定】
对于 30%的数据, t10, x_1,y_1,x_2,y_2100。
对于 60%的数据, t1000, x_1,y_1,x_2,y_2104
对于 100%的数据, t104, x_1,y_1,x_2,y_2109
果断暴力骗了30分= =
后来去想优化还是没来得及写出来
60分只要单纯枚举行列就可以了不用逐个枚举
满分做法看了我瞎了
技术分享
技术分享
技术分享
竟然对表格分块我也真是醉了
之前没学分块昨天学姐才刚讲了讲知道了能把线性表分块
表格竟然也可以OTZ
暴力我就不贴了QwQ

//std by faebdc 这高精写的我也醉了
#include<cstdio>
#include<cstring>
const int N = 100;
const int M = 100000;
long long ans[N];
long long now[N];
long long jie[N];
void clear(long long x[])
{
    int i;
    for(i=1;i<=x[0];i++)
        x[i]=0;
    x[0]=0;
}
void add(long long x[],int y)
{
    x[1]+=y;
    int i=1;
    while(x[i]>=M)
    {
        x[i+1]+=x[i]/M;
        x[i]%=M;
        i++;
    }
    if(x[0]<i)
        x[0]=i;
}
void mul(long long x[],int y)
{
    int i;
    for(i=1;i<=x[0];i++)
        x[i]*=y;
    for(i=1;i<=x[0];i++)
    {
        x[i+1]+=x[i]/M;
        x[i]%=M;
    }
    while(x[x[0]+1])
    {
        x[0]++;
        x[x[0]+1]+=x[x[0]]/M;
        x[x[0]]%=M;
    }
    while(x[0] && !x[x[0]])
        x[0]--;
}
void jia(long long x[],long long y[])
{
    int i;
    if(x[0]<y[0])
        x[0]=y[0];
    for(i=1;i<=x[0];i++)
        x[i]+=y[i];
    for(i=1;i<=x[0];i++)
    {
        x[i+1]+=x[i]/M;
        x[i]%=M;
    }
    while(x[x[0]+1])
    {
        x[0]++;
        x[x[0]+1]+=x[x[0]]/M;
        x[x[0]]%=M;
    }
}
void jian(long long x[],long long y[])
{
    int i;
    for(i=1;i<=x[0];i++)
        x[i]-=y[i];
    for(i=1;i<=x[0];i++)
    {
        if(x[i]<0)
        {
            x[i]+=M;
            x[i+1]--;
        }
    }
    while(x[0] && !x[x[0]])
        x[0]--;
}
void divide(long long x[],int y)
{
    int i;
    long long has=0;
    for(i=x[0];i>0;i--)
    {
        has*=M;
        has+=x[i];
        x[i]=has/y;
        has%=y;
    }
    while(x[0] && !x[x[0]])
        x[0]--;
}
void print(long long x[])
{
    int i;
    int a=x[1];
    int b=x[2];
    if(x[0]==1)
        printf("%d\n",a);
    else if(x[0]==2)
    {
        printf("%d",b);
        printf("%05d\n",a);
    }
    else
    {
        printf("...%05d",b);
        printf("%05d\n",a);
    }
}
void yici(long long x[],int y)
{
    clear(x);
    add(x,y);
    mul(x,y+1);
    divide(x,2);
}
void erci(long long x[],int y)
{
    clear(x);
    add(x,y);
    mul(x,y+1);
    mul(x,(y<<1)+1);
    divide(x,6);
}
void sanci(long long x[],int y)
{
    clear(x);
    add(x,y);
    mul(x,y);
    mul(x,y+1);
    mul(x,y+1);
    divide(x,4);
}
void same(int a,int b,int c,int d)
{
    int s=a-1;
    d-=s;
    yici(now,s);
    mul(now,d);
    mul(now,d<<1);
    jia(ans,now);
    erci(now,d);
    mul(now,2);
    yici(jie,d);
    mul(jie,3);
    add(now,d);
    jian(now,jie);
    mul(now,s<<1);
    jia(ans,now);
    sanci(now,d);
    mul(now,2);
    yici(jie,d);
    jia(now,jie);
    erci(jie,d);
    mul(jie,3);
    jian(now,jie);
    jia(ans,now);
    clear(now);
    add(now,d);
    mul(now,d);
    jia(ans,now);
}
void transverse(int a,int b,int c,int d)
{
    clear(now);
    add(now,a);
    mul(now,a<<1);
    clear(jie);
    add(jie,b-1);
    add(jie,d-1);
    jian(now,jie);
    d=d-b+1;
    b=c-a+1;
    mul(now,b);
    mul(now,d);
    divide(now,2);
    jia(ans,now);
    yici(now,b-1);
    mul(now,a<<1);
    mul(now,d);
    jia(ans,now);
    yici(now,b);
    mul(now,b*2+1);
    erci(jie,b);
    mul(jie,2);
    jian(now,jie);
    clear(jie);
    add(jie,b);
    mul(jie,b);
    jian(now,jie);
    mul(now,d);
    jia(ans,now);
}
void lengthways(int a,int b,int c,int d)
{
    clear(now);
    add(now,a);
    add(now,c);
    c=c-a+1;
    a=b-1;
    b=d-b+1;
    clear(jie);
    add(jie,a);
    mul(jie,a<<1);
    jia(now,jie);
    mul(now,c);
    divide(now,2);
    mul(now,b);
    jia(ans,now);
    yici(now,b-1);
    mul(now,a<<1);
    mul(now,c);
    jia(ans,now);
    yici(now,b);
    mul(now,b*2+1);
    erci(jie,b);
    mul(jie,2);
    jian(now,jie);
    clear(jie);
    add(jie,b);
    mul(jie,b);
    jian(now,jie);
    mul(now,c);
    jia(ans,now);
}
void regular(int a,int b,int c,int d)
{
    if(a>b)
    {
        transverse(a,b,c,a-1);
        same(a,a,c,d);
    }
    else if(a<b)
    {
        lengthways(a,b,b-1,d);
        same(b,b,c,d);
    }
    else
        same(a,b,c,d);
}
void rectangle(int a,int b,int c,int d)
{
    if(b>=c)
        lengthways(a,b,c,d);
    else if(a>=d)
        transverse(a,b,c,d);
    else if(c<d)
    {
        regular(a,b,c,c);
        lengthways(a,c+1,c,d);
    }
    else if(c>d)
    {
        regular(a,b,d,d);
        transverse(d+1,b,c,d);
    }
    else
        regular(a,b,c,d);
    print(ans);
}
void f()
{
    memset(ans,0,sizeof ans);
    int a,b,c,d;
    scanf("%d%d%d%d",&a,&b,&c,&d);
    rectangle(a,b,c,d);
}
int main()
{
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
        f();
    return 0;
}

T3 CF342D Xenia and Dominoes
Xenia 非常喜欢拼图。她特别喜欢由多米诺骨牌组成的拼图。下图就是这样的拼图。
技术分享
一个拼图是一个 3× n 的网格,除掉一些禁止块(图中的黑色正方形),并包含多米
诺骨牌。一个拼图被称作合法当它符合以下条件:
1、每个多米诺骨牌覆盖正好两个非禁止块;
2、没有两个多米诺骨牌覆盖网格上的同一块区域;
3、有且仅有一个非禁止块没被任何多米诺骨牌覆盖(图中的圆点)。
为了使拼图有意义, 拼图必须至少能移动一步。 一步移动是在保证拼图合法的情况下,
把一个多米诺骨牌移到空格子里。 横向的多米诺骨牌只能横向移动, 纵向的多米诺骨牌只
能纵向移动。你不能旋转多米诺骨牌。图中表示了一个合法的移动。
Xenia 有一个 3× n 的带禁止块和一个圆圈标记的网格。 Xenia 还有很多完全一样的多
米诺骨牌。 现在 Xenia 想知道, 如果她把多米诺骨牌放在网格上, 能有多少种不同的合法
的拼图。 同时, Xenia 要求圆圈标记的格子没有被覆盖。 这个拼图还必须至少能移动一次。
帮助 Xenia 统计上述拼图的种数。这个数字可能很大,输出它对 1000000007(109+7)
取模的余数。
【输入格式】
第一行一个整数 n 表示拼图的大小。 接下来 3 行, 每行 n 个字符, 描述这个网格。 “X”
表示禁止块, “. ” 表示非禁止块, “O” 表示它是圆圈标记的。
保证有且仅有一个格子是圆圈标记的。
【输出格式】
输出一个整数,问题的答案对 1000000007(109+7) 取模的余数。
【样例输入】
5
….X
.O…
…X.
【样例输出】
1
【数据规模和约定】
对于 30%的数据, n≤8
对于 60%的数据, n≤1000
对于 90%的数据, n≤50000
对于 100%的数据, n≤2000000
暴力当时都写残了
想了想不改了直接输出0得了说不定能骗到分
没想到一分没有QAQ
90分是插头DP(轮廓线?)(不会写)
100分对90分加了优化,减少常数和内存(比如滚动数组)
技术分享
反正我是看了题解也不会写…
(一定有人嘲讽你前两天网络课插头DP白学了)
反正我就是弱就是蠢QAQ

//std by faebdc
#include<cstdio>
const int N = 2000500;
const int T = 8;
const int MOD = 1000000007;
const int dir[4][2]={ {0,-1},{-1,0},{0,1},{1,0} };
int dp[2][T];
bool map[N][5];
int ans=0;
int zx,zy;
int n;
int Do()
{
    int i,j;
    int ii=0;
    for(i=0;i<=6;i++)
        dp[0][i]=0;
    dp[0][7]=1;
    for(i=1;i<=n;i++)
    {
        ii^=1;
        int has=7;
        if(map[i][1])
            has^=1;
        if(map[i][2])
            has^=2;
        if(map[i][3])
            has^=4;
        for(j=0;j<8;j++)
        {
            if((j&has)==has)
            {
                int add=j^has;
                dp[ii][j]=dp[ii^1][7^add];
                if(add==3 || add==6)
                {
                    dp[ii][j]+=dp[ii^1][7];
                    if(dp[ii][j]>=MOD)
                        dp[ii][j]-=MOD;
                }
                if(add==7)
                {
                    dp[ii][j]+=dp[ii^1][3];
                    if(dp[ii][j]>=MOD)
                        dp[ii][j]-=MOD;
                    dp[ii][j]+=dp[ii^1][6];
                    if(dp[ii][j]>=MOD)
                        dp[ii][j]-=MOD;
                }
            }
            else
                dp[ii][j]=0;
        }
    }
    return dp[n&1][7];
}
void dfs(int x,int has)
{
    if(x>3)
    {
        if(!has)
            return;
        if(has&1)
        {
            ans+=Do();
            if(ans>=MOD)
                ans-=MOD;
        }
        else
        {
            ans-=Do();
            if(ans<0)
                ans+=MOD;
        }
        return;
    }
    dfs(x+1,has);
    int tx=dir[x][0];
    int ty=dir[x][1];
    if(map[zx+tx][zy+ty] && map[zx+tx+tx][zy+ty+ty])
    {
        map[zx+tx][zy+ty]=0;
        map[zx+tx+tx][zy+ty+ty]=0;
        dfs(x+1,has+1);
        map[zx+tx][zy+ty]=1;
        map[zx+tx+tx][zy+ty+ty]=1;
    }
}
int main()
{
    freopen("xenia.in","r",stdin);
    freopen("xenia.out","w",stdout);
    int i,j;
    scanf("%d",&n);
    for(j=1;j<=3;j++)
    {
        char t;
        scanf(" %c",&t);
        for(i=1;i<=n;i++)
        {
            if(t==‘O‘)
            {
                zx=i;
                zy=j;
            }
            if(t==‘.‘)
                map[i][j]=1;
            t=getchar();
        }
    }
    dfs(0,0);
    printf("%d\n",ans);
    return 0;
}

今天虽然没爆零但是估计是RP好罢了QAQ
明天还是要做好爆零的准备QAQ
但是以后至少要把简单暴力分拿到啊!

【学长虐学弟欢乐赛】Round3

标签:随便搞搞

原文地址:http://blog.csdn.net/creationaugust/article/details/44020355

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!