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);
}