Codeforces Round #545 (Div. 1)
Codeforces Round #545 (Div. 1)
FST x 2 成功下紫
T1
题意:一个\(n*m\)的矩阵,要求对于上面的每一个值,对他所在的行和列进行离散化,使得对于行和列都满足大小关系不变,让最大的值最小
题解:记一下每个值在行上列上的排名以及有几种排名大于它,两个数分别取\(max\)相加即可
代码:
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
int ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=ans*10+ch-'0',ch=getchar();
return ans*fh;
}
const int maxn=1100;
int n,m,a[maxn][maxn],d1[maxn][maxn],d2[maxn][maxn];
int c1[maxn],c2[maxn];
pair<int,int>p[maxn];
int main(){
// freopen("nh.in","r",stdin);
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) a[i][j]=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
p[j]=make_pair(a[i][j],j);
sort(p+1,p+m+1);
int now=0;
for(int j=1;j<=m;j++){
if(p[j].first!=p[j-1].first)
now++;
d1[i][p[j].second]=now;
}
c1[i]=now;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)
p[j]=make_pair(a[j][i],j);
sort(p+1,p+n+1);
int now=0;
for(int j=1;j<=n;j++){
if(p[j].first!=p[j-1].first)
now++;
d2[p[j].second][i]=now;
}
c2[i]=now;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int mx=max(d1[i][j],d2[i][j]);
int my=max(c1[i]-d1[i][j],c2[j]-d2[i][j]);
printf("%d ",mx+my);
}
printf("\n");
}
return 0;
}
T2
题意:给你两个\(01\)串\(s,t\),\(s\)可以随意排列,问如何排列\(s\)使得\(t\)在\(s\)上的匹配点更多
吐槽:一个细节写错FST了...
题解:用\(kmp\)算出\(t[n]\)的\(nex\),既然\(s\)可以随意排列,就把它的\(0,1\)全部拿走再一个一个插入。那么最好的插入方法就是:先插入一个\(t\)串,再一直插\(t[nex+1\sim len[t]]\)。如果要用的字符没了就填另一种直到全部用完
代码:
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
int ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=ans*10+ch-'0',ch=getchar();
return ans*fh;
}
const int maxn=5e5+100;
int n,m,kmp[maxn],c[2],cnt[2],len,d[2];
char s[maxn],t[maxn],ans[maxn];
inline void getnex(){
int j=0;
for(int i=2;i<=m;i++){
while(j&&t[i]!=t[j+1]) j=kmp[j];
if(t[j+1]==t[i]) j++;
kmp[i]=j;
}
}
int main(){
// freopen("nh.in","r",stdin);
// freopen("zhy.out","w",stdout);
scanf("%s%s",s+1,t+1);
n=strlen(s+1),m=strlen(t+1);
getnex();
for(int i=1;i<=n;i++){
if(s[i]=='1') cnt[1]++;
else cnt[0]++;
}
for(int i=1;i<=m;i++){
if(t[i]=='1') d[1]++;
else d[0]++;
}
int len=n;
for(int i=1;i<=kmp[m];i++){
if(!cnt[0]&&!cnt[1]) return 0;
int x=t[i]-'0';
if(!cnt[x]) x^=1;
printf("%c",'0'+x),cnt[x]--;
}
while(1){
for(int i=kmp[m]+1;i<=m;i++){
if(!cnt[0]&&!cnt[1]) return 0;
int x=t[i]-'0';
if(!cnt[x]) x^=1;
printf("%c",'0'+x),cnt[x]--;
}
}
return 0;
}
T3
题意:有\(n\)个点\(m\)条边的有向图,从\(1\)号点开始每次可以走到相连的点,设第\(x\)步走到了点\(p\),如果\(p[(x-1)\%d+1]==1\)这个点就可以产生一次贡献,每个点最多产生一次贡献,问最多能产生多少贡献(可以走无限步)
吐槽:数组开太大MLE了(什么玩意)
题解:分层图 + \(tarjan\)强连通分量
按时间分为\(d\)层,原图的一条边\((x,y)\)就变成了分层图中的\((p[x][k],p[y][k\%d+1])\)。可以发现对于原图存在的强连通分量,新的图也一定存在(像螺旋一样的但一定能接起来)。我们就可以对于分层图的每个强连通分量统计它的贡献(即其中不同点的个数),缩点后按照\(tarjan\)拓扑逆序\(dp\)一下就好了
至于为什么不会存在两个强连通分量相连并且都有某个原图中的点使得贡献算重...首先在分层图上不同层间的同一个点肯定是不会互相连边的吧,然后如果是一个强连通分量中的另一个的连向了这个强连通分量,由于这两个强连通分量都有同一个点,所以其实他们是一个强连通分量...(说的有点迷)
代码:
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
int ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=ans*10+ch-'0',ch=getchar();
return ans*fh;
}
const int maxn=5e6+100,maxm=5e6+100;
int n,m,d,head[maxn],nex[maxm],v[maxm],num=1,ans[maxn];
int tmp[100100],f[maxn],ok[maxn];
int low[maxn],dfn[maxn],tim,tp,st[maxn],bc[maxn],btot;
int u[maxm],mx;
char s[100];
inline int p(int x,int y){return (x-1)*d+y;}
inline int bh(int x){return (x-1)/d+1;}
inline void add(int x,int y){
u[++num]=x,v[num]=y;
nex[num]=head[x],head[x]=num;
}
void dfs(int x){
low[x]=dfn[x]=++tim,st[++tp]=x;
for(int i=head[x];i;i=nex[i]){
int y=v[i];
if(!dfn[y]) dfs(y),qmin(low[x],low[y]);
else if(!bc[y]) qmin(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
++btot;
int pp=tp;
while(1){
int y=st[pp--];
bc[y]=btot;
if(y==x) break;
}
int cnt=0;
for(int i=pp+1;i<=tp;i++){
int y=st[i];if(!ok[y]) continue;
if(!tmp[bh(y)]) tmp[bh(y)]=1,cnt++;
}
while(1){
int y=st[tp--];
if(ok[y]) tmp[bh(y)]=0;
if(y==x) break;
}
ans[btot]=cnt;
}
}
inline void rebuild(){
int numm=num;
memset(head,0,sizeof(head)),num=1;
for(int i=2;i<=numm;i++){
int x=bc[u[i]],y=bc[v[i]];
if(x==y) continue;
add(x,y);
}
}
inline void dp(){
for(int i=1;i<=btot;i++) {
for(int j=head[i];j;j=nex[j])
qmax(f[i],f[v[j]]);
f[i]+=ans[i];
}
// while(!q.empty()){
// int x=q.front();q.pop();
// for(int i=head[x];i;i=nex[i]){
// int y=v[i];du[y]--;
// qmax(f[y],f[x]+ans[y]);
// if(!du[y]) q.push(y);
// }
// }
// int Ans=0;
// for(int i=1;i<=btot;i++)
// qmax(Ans,f[i]);
printf("%d\n",f[bc[1]]);
}
int main(){
freopen("nh.in","r",stdin);
freopen("zhy.out","w",stdout);
n=read(),m=read(),d=read();
mx=n*d;
for(int i=1;i<=m;i++){
int x=read(),y=read();
for(int j=1;j<d;j++)
add(p(x,j),p(y,j+1));
add(p(x,d),p(y,1));
}
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=d;j++)
if(s[j]=='1') ok[p(i,j)]=1;
}
for(int i=1;i<=mx;i++)
if(!dfn[i]) dfs(i);
rebuild(),dp();
return 0;
}
感想:
像\(T2\)这种细节题要先想清楚再写
像\(T3\)这种题...别太浪了不重要的数组不要随便加
哎,又掉回紫名了,还是太菜了啊