Codeforces

Codeforces Round #439 (Div. 2)

情况:菜啊!只做了A、B两道题,凭借hack两个人,勉强上了一点分……
赛后更正了C、E。

C:

可以这样考虑:任意两种颜色互相连边是对第三种颜色没有影响的,所以我们可以先算出任意两种颜色连边的方案数,然后再乘起来。work(a,b)表示其中一种颜色有a个,另外一种颜色有b个的连边方案,然后我们枚举每种颜色选多少个点,加起来即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=5010;
const LL mod=998244353;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int a,b,c;
LL fac[5010],fin[5010],inv[5010];
void pre()
{
    fac[0]=inv[0]=fin[0]=fac[1]=inv[1]=fin[1]=1;
    for(int i=2;i<=5000;i++)
    {
        fac[i]=fac[i-1]*(LL)(i)%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fin[i]=fin[i-1]*inv[i]%mod;
    }
}
LL C(int x,int y){return fac[x]*fin[y]%mod*fin[x-y]%mod;}
LL get(int a,int b,int i){return C(a,i)*C(b,i)%mod*fac[i]%mod;}
LL work(int a,int b)
{
    LL ans=0;
    for(int i=0;i<=min(a,b);i++)
    ans=(ans+get(a,b,i))%mod;
    return ans;
}
int main()
{
    pre();
    a=read();b=read();c=read();
    printf("%I64d",work(a,b)*work(a,c)%mod*work(b,c)%mod);
}

E:

赛后看了下别人的代码,又感觉并不难。就是用二维树状数组维护hash值,判断两个点是否在同一区域的话,就求两点的hash值是否相同,设置障碍区间加,删除障碍区间减,树状数组就是区间修改单点查询就行了,二维差分自己想想就明白了。一开始写的是mod一个数的hash,疯狂WA,改为自然溢出后才AC。

代码:

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
map<pair< pa,pa >,int >h;
const int Maxn=2510;
const int base=233;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int s[Maxn][Maxn],Pow[100010];
int n,m,q;
void add(int x,int y,int z)
{
    for(int i=x;i<=n;i+=(i&-i))
    for(int j=y;j<=m;j+=(j&-j))
    s[i][j]+=z;
}
int getsum(int x,int y)
{
    int re=0;
    for(int i=x;i;i-=(i&-i))
    for(int j=y;j;j-=(j&-j))
    re+=s[i][j];
    return re;
}
int main()
{
    n=read();m=read();q=read();
    Pow[0]=1;for(int i=1;i<=100000;i++)Pow[i]=Pow[i-1]*base;
    while(q--)
    {
        int op=read(),r1=read(),c1=read(),r2=read(),c2=read();
        if(op==1)
        {
            h[make_pair(make_pair(r1,c1),make_pair(r2,c2))]=q;
            add(r1,c1,Pow[q]);
            add(r2+1,c1,-Pow[q]);
            add(r1,c2+1,-Pow[q]);
            add(r2+1,c2+1,Pow[q]);
        }
        else if(op==2)
        {
            int o=h[make_pair(make_pair(r1,c1),make_pair(r2,c2))];
            add(r1,c1,-Pow[o]);
            add(r2+1,c1,Pow[o]);
            add(r1,c2+1,Pow[o]);
            add(r2+1,c2+1,-Pow[o]);
        }
        else
        {
            int n1=getsum(r1,c1),n2=getsum(r2,c2);
            if(n1==n2)puts("Yes");
            else puts("No");
        }
    }
}

Codeforces Round #441 (Div. 2, by Moscow Team Olympiad)

情况:一个小时做完ABCD,剩下时间想做E,结果自己偷懒没看题目,听羊老师讲,自己理解错了……真蛋疼……这场似乎不难?

E:

容易知道哪些数字是必须改变的,哪些是不能改变的,哪些是一个变了另外一个也必须变的。必须变或者不变就Mark一下,一个变了另外一个必须变那么就连边,因为一定是大的数字连向小的数字,所以一定是一个DAG,有向无环图,所以最后按照拓扑序,传递一下标记,看看有没有矛盾即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=100010;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,degree[Maxn];
int mark[Maxn];
vector<int>a[Maxn];
struct Edge{int y,next;}e[Maxn];
int last[Maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
map<int,map<int,bool> >mp;
queue<int>q;
int main()
{
    memset(mark,-1,sizeof(mark));
    n=read();m=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        for(int j=1;j<=x;j++)
        a[i].push_back(read());
    }
    for(int i=2;i<=n;i++)
    {
        int l1=a[i-1].size(),l2=a[i].size();bool same=true;
        for(int j=0;j<min(l1,l2);j++)
        {
            int x=a[i-1][j],y=a[i][j];
            if(x!=y)
            {
                if(x<y)
                {
                    if(mark[x]==0&&mark[y]==1){puts("No");return 0;}
                    if(!mp[y][x])ins(y,x),mp[y][x]=true,degree[x]++;
                }
                else
                {
                    if(mark[x]==0){puts("No");return 0;}
                    mark[x]=1;
                    if(mark[y]==1){puts("No");return 0;}
                    mark[y]=0;
                }same=false;break;
            }
        }
        if(same&&l1>l2){puts("No");return 0;}
    }
    for(int i=1;i<=m;i++)if(!degree[i])q.push(i);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=last[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(mark[x]!=-1)
            {
                if(mark[x]==1)
                {
                    if(mark[y]==0){puts("No");return 0;}
                    mark[y]=mark[x];
                }
            }
            degree[y]--;if(!degree[y])q.push(y);
        }
    }
    puts("Yes");int cnt=0;
    for(int i=1;i<=m;i++)
    if(mark[i]>0)cnt++;
    printf("%d\n",cnt);
    for(int i=1;i<=m;i++)
    if(mark[i]>0)printf("%d ",i);
}

F:

先用单调栈搞出i这个位置作为最大值最左最右能延伸到哪里,然后再搞出i这个位置向左第一个让它变大的位置和向右第一个让它变大的位置,然后讨论一下即可,注意细节。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=200010;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int sta[Maxn],top=0;
int n,a[Maxn],ls[Maxn],rs[Maxn],ls1[Maxn],rs1[Maxn],lt[Maxn],rt[Maxn];
int o;
int g(int x){return((x>>o)&1);}
int main()
{
    memset(ls1,0,sizeof(ls1));
    memset(rs1,63,sizeof(rs1));
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
    {
        while(top&&a[sta[top]]<a[i])rs[sta[top--]]=i-1;
        ls[i]=sta[top]+1;sta[++top]=i;
    }
    while(top)rs[sta[top--]]=n;
    for(int j=0;j<=30;j++)
    {
        o=j;
        lt[1]=g(a[1]);
        for(int i=2;i<=n;i++)
        if(g(a[i]))lt[i]=i;
        else lt[i]=lt[i-1];
        rt[n]=((g(a[n])==1)?n:n+1);
        for(int i=n-1;i;i--)
        if(g(a[i]))rt[i]=i;
        else rt[i]=rt[i+1];
        for(int i=1;i<=n;i++)
        if(g(a[i])==0)ls1[i]=max(ls1[i],lt[i]),rs1[i]=min(rs1[i],rt[i]);
    }
    LL ans=0;
    //ls[i]做最大值延伸最左 rs[i]做最大值延伸最右
    //ls1[i]左边第一个1没有则为0 rs1[i]右边第一个1没有则n为n+1
    for(int i=1;i<=n;i++)
    {
        if(ls1[i]>=ls[i])ans+=(LL)(ls1[i]-ls[i]+1)*(LL)(min(rs[i],rs1[i]-1)-i+1);
        if(rs1[i]<=rs[i])ans+=(LL)(rs[i]-rs1[i]+1)*(LL)(i-max(ls[i],ls1[i]+1)+1);
        if(ls1[i]>=ls[i]&&rs1[i]<=rs[i])ans+=(LL)(ls1[i]-ls[i]+1)*(LL)(rs[i]-rs1[i]+1);
    }
    printf("%I64d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值