A. Skyscrapers
给出n*m个数,每个位置都有相应的数值,然后对于每一个位置,
将该数字行、列的数字单独拿出来离散化后
(该离散化只需要满足行中数的大小关系不变,列中数的大小关系不变),
最大的那个值。

n,m<=1000,aij<=109n,m<=1000,a_{ij}<=10^9

水题。
按照题意模拟+预处理优化即可。

#include<bits/stdc++.h>
#define maxn 2005
using namespace std;

int n,m,a[maxn][maxn],c[maxn],b[2][maxn][maxn],mxh[maxn],mxl[maxn];

int D;
inline bool cmp(const int &u,const int &v){ return a[D][u] < a[D][v]; }
inline bool cmp2(const int &u,const int &v){ return a[u][D] < a[v][D]; }

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) c[i] = i;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
	{
		D = i;
		sort(c+1,c+1+m,cmp);
		for(int j=1;j<=m;j++)
		{
			mxh[i] = b[0][i][c[j]] = (a[i][c[j]] == a[i][c[j-1]] ? b[0][i][c[j-1]] : b[0][i][c[j-1]]+1);
		}
	}
	
	for(int i=1;i<=n;i++) c[i] = i;
	for(int i=1;i<=m;i++)
	{
		D = i;
		sort(c+1,c+1+n,cmp2);
		for(int j=1;j<=n;j++)
			mxl[i] = b[1][c[j]][i] = (a[c[j]][i] == a[c[j-1]][i] ? b[1][c[j-1]][i] : b[1][c[j-1]][i]+1);
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			printf("%d%c",max(mxl[j],max(mxh[i],max(b[0][i][j]+mxl[j]-b[1][i][j],b[1][i][j]+mxh[i]-b[0][i][j]))),j==m?'\n':' ');
		}
	}
}

B. Camp Schedule
首先贪心,我们把S中的01一直摆成T。
又因为可以重叠,所以我们用KMP求一下最长Border(最长的可以前后共用的最长公共前后缀),然后前T和后T共用一下节省字符。
然后就完了。

#include<bits/stdc++.h>
#define maxn 500005
using namespace std;

char s[maxn],t[maxn];
int n,m,c0,c1,nxt[maxn],u0,u1,uc0,uc1;

int main()
{
	scanf("%s",s);
	scanf("%s",t);
	n = strlen(s) , m = strlen(t);
	for(int i=0;i<n;i++) 
		if(s[i] == '0') c0++;
		else c1++; 
	nxt[0] = -1;
	for(int k=0,j=-1;k<m;)
		if(j == -1 || t[k] == t[j]) nxt[++k] = ++j;
		else j = nxt[j];
		
	int len = nxt[m];
	for(int i=0;i<m;i++)
		if(t[i] == '0') u0++;
		else u1++; 
	
	for(int i=nxt[m];i<m;i++)
		if(t[i] == '0') uc0++;
		else uc1++; 
		
	if(c0<u0 || c1<u1)
	{
		puts(s);
		return 0;
	}
	c0-=u0,c1-=u1;
	int now = m;
	for(int i=0;i<m;i++) 
		s[i] = t[i];
	for(;c0>=uc0 && c1>=uc1;)
	{
		c0 -= uc0 , c1 -= uc1;
		for(int j=0;j<m-nxt[m];j++)
			s[now+j] = t[j+nxt[m]];
		now += m-nxt[m];
	}
	for(int i=now;i<n;i++)
		if(c0) s[i] = '0' , c0--;
		else s[i] = '1' , c1--;
	puts(s);
}

C. Museums Tour
n个城市,m条(单向)路,一周有d天。
每个城市有一座博物馆,只在一周中的某几(十)天开放。
每天的晚上你可以走一条路,早上到,中午可以选择参观(开放的)博物馆。
星期一从第1个城市出发,问最多可以参观多少个不同的博物馆。
n,m<=10^5,d<=50

考虑拆点。
把每个城市的每天拆成一个点,然后求强联通分量缩点然后DAG求最长路。
发现可能会同一城市不同天算重。
考虑从(a市,星期x)走到(a,y)
那么(a,x)->(a,x+(y-x) % d)->(a,x+2*(y-x) % d)->…(a,x+d*(y-x) % d)即(a,x)
那么(a,x)和(a,y)一定在同一强联通分量中,
那么我们只需要在计算每个强联通分量包含博物馆数量时去重就行了。
这个可以通过打vis解决。
注意这个题卡内存。。。。。。

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
#include<map>
using namespace std;
const int maxn=5e6+5;
struct Edge{
	int to,nxt;
}edge[maxn];
int head[maxn];
int tot;
void addedge(int u,int v){
	tot++;
	edge[tot].to=v;
	edge[tot].nxt=head[u];
	head[u]=tot;
}
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc;
int w[maxn];
int S[maxn],top;
int n,m,d;
char s[100005][55];
int vis[100005];
void dfs(int u) {
	pre[u]=lowlink[u]=++dfs_clock;
	S[++top]=u;
	for(int i=head[u]; i; i=edge[i].nxt) {
		int v=edge[i].to;
		if(!pre[v]) {
			dfs(v);
			lowlink[u]=min(lowlink[u],lowlink[v]);
		} else if(!sccno[v]) {
			lowlink[u]=min(lowlink[u],pre[v]);
		}
	}
	if(lowlink[u]==pre[u]) {
		scc++;
		for(;;) {	
			int x;		
			x=S[top--];	
			sccno[x] = scc;
			int D = (x+n-1)/n-1,E=(x-1)%n+1;
			if(s[E][D]=='1'&&vis[E]!=scc)vis[E]=scc,w[scc]++;
			if(x==u)break;
		} 
	}
}
void find_scc(int n) {
	for(int i=1; i<=n; i++) 
		if(!pre[i])dfs(i);
}
int idx(int x,int y){
	return y*n+x;
}
int X[100005],Y[100005];
int dp[maxn];
void Solve(int u){
	dp[u]=w[u];
	for(int i=head[u];i;i=edge[i].nxt){
		int v = edge[i].to;
		if(dp[v]==-1)Solve(v); 
		dp[u]=max(dp[u],dp[v]+w[u]); 
	}
}
int main() {
	cin>>n>>m>>d;
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		X[i]=u;Y[i]=v;
		for(int j=0;j<d;j++){
			addedge(idx(u,j),idx(v,(j+1)%d));
		}	
	}
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]);
	find_scc(n*d);
	memset(head,0,sizeof(head));
	tot=0;
	for(int i=1;i<=m;i++){
		for(int j=0;j<d;j++)
		if(sccno[idx(X[i],j)]!=sccno[idx(Y[i],(j+1)%d)]){
			addedge(sccno[idx(X[i],j)],sccno[idx(Y[i],(j+1)%d)]);
		}
	}
	memset(dp,-1,sizeof(dp));
	Solve(sccno[idx(1,0)]);
	printf("%d\n",dp[sccno[idx(1,0)]]);
	return 0;
}

D. Cooperative Game

Codeforces Round #545 (Div. 1)

十个人在家中,有如图的链套环的一个图(t与c未知),你每次可以让一些人走一步,
交互库会返回当前的人分成了几堆,每堆有哪些人,
在3(t+c)步之内要让所有人走到插旗子的那个点上并输出done。
t,c<=1000
思博???

首先应该想到判环。
如果贪常数用b开头的某判环算法就会死的很惨。
因为如果你Pollard-Rho打的不好,用的是floyd判环的话。
让0每次走1步,1每次走两步,那么

Codeforces Round #545 (Div. 1)

注意这里的C代表的是环长的整数倍。
小圈为0和1的相遇点。
惊奇的发现只需要每个点都再走t步即可到达终点,并不需要知道t是多少,所有点相遇即可。

#include<iostream>
using namespace std;
char s[100];
int cnt(){int x,p;scanf("%d",&x);for(p=x;p;p--) scanf("%s",s);return x;}
int main()
{
    do{
        puts("next 0");fflush(stdout);cnt();
        puts("next 0 1");fflush(stdout);
    }while(cnt()!=2);
    do{
        puts("next 0 1 2 3 4 5 6 7 8 9");
        fflush(stdout);
    }while(cnt()!=1);
    puts("done");fflush(stdout);
}

E. Train Car Selection!
长为n的火车,支持下面3个操作,共m次。
1:在前面加k节车厢,值为0。
2:在后面加k节车厢,值为0 。
3:所有车厢都加值,对于从前往后第i个车厢,这个值为B+S(i-1),1<=B,S
每次操作后求车厢的最小值和取最小值时最靠前的车厢。

n&lt;=109,m&lt;=3105n&lt;=10^9,m&lt;=3*10^5

发现如果在前面加车厢,之前的所有车厢都不可能是最优解,(不)相当于清零。
如果在后面加车厢呢?
只有加的第一节有可能是最优解。
所以我们不用考虑n(10^9)节车厢而只需要考虑第一节和加的第一节。
对于全局加等差数列,我们可以用数组+全局标记这一高级数据结构来维护。
新加入的可以自己自适应一下使得它满足全局标记。
然后发现每个第一节可以看做一个点(x,y)我们需要求sums*x+y的最小值。
斜率递减横坐标单增可以用栈来维护最值点。

#include<bits/stdc++.h>
#define maxn 600006
#define LL long long
using namespace std;

int n,m;
struct Point{
	LL x,y;
	Point(LL x=0,LL y=0):x(x),y(y){}
	Point operator +(const Point &B)const{ return Point(x+B.x,y+B.y); }
	Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
	double operator *(const Point &B)const{ return 1.0*x*B.y-1.0*y*B.x; }
}P[maxn];
int L,R;
LL ab,as;

int main(){
	scanf("%d%d",&n,&m);
	L=R=300001;
	P[R] = Point(0,0);
	for(int a,b,s;m--;){
		scanf("%d%d",&a,&b);
		if(a==3){
			scanf("%d",&s);
			ab = b + ab;
			as = as + s;
		}
		else if(a==1){
			P[L=R=300001]=Point(0,0);
			n+=b,ab=as=0;
		}
		else{
			Point tmp = Point(n,-ab-as*n);n += b;
			for(;R-L+1>1 && (tmp-P[R]) * (P[R]-P[R-1])>=0;R--);
			P[++R] = tmp;
		}
		for(;R-L+1>1 && (P[R].y-P[R-1].y)>=(P[R].x-P[R-1].x)*(-as);R--);
		printf("%I64d %I64d\n",P[R].x+1,P[R].y+P[R].x*as+ab);
	}
}

F. Matches Are Not a Child’s Play
n个点,一颗无根树。每次找最小标号的叶子节点然后删除。
得到一个删除序列。
支持
1:把某个点的标号设为当前最大标号+1
2:求某点在删除序列中的排名。
3:求某两点在删除序列中的先后。
n,m<=2*10^5

3本质就是两次2.
1的本质。理解。
把那个点u和之前的最后一个删除点v之间的路径保护到其他点都被删完。
然后从v删到u,u成为新的最后一个删除点。
这个保护,我们可以看做这条链上的所有点都变成了标号最大的点。
那么对于一个点来说它在删除序列中的排名就是标号比他小的点的数量+以最后一个删除点为根,深度比他大且标号与他相等的点的数量。
那么我们要的操作就是链赋值和求标号的区间和,以及链上标号区间和。
(其实我也不知道我上面想说什么)也许可以树链剖分。
但是利用LCTaccess的性质可以得到更好的解法。

发现这个链赋值和LCT的把一条链变成重链是一样的。
每个splay中的点都是同色的。
然后你就发现可以在access中完成维护。
还可以很方便的换根。
一点都不难受

#include<bits/stdc++.h>
#define maxn 400005
using namespace std;

int n,q;
int tr[maxn];
inline void Upd(int now,int val){ for(;now<maxn;now+=now&-now) tr[now]+=val; }
inline int qsum(int now,int ret=0){ for(;now;now-=now&-now) ret+=tr[now];return ret; }

namespace LCT{
	int fa[maxn]={},ch[maxn][2]={},siz[maxn]={},rev[maxn]={},col[maxn]={},tag[maxn]={};
	#define il inline 
	#define pa fa[x]
	il int inr(int x){ return ch[pa][1] == x; }
	il int isr(int x){ return ch[pa][0]!=x && ch[pa][1]!=x; }
	il void dt(int x){
		if(rev[x]){
			swap(ch[x][0],ch[x][1]),rev[x]=0;
			if(ch[x][0]) rev[ch[x][0]] ^= 1;
			if(ch[x][1]) rev[ch[x][1]] ^= 1;
		}
		if(tag[x] && x){
			col[x] = tag[ch[x][0]] = tag[ch[x][1]] = tag[x];
			tag[x] = 0;
		}
	}
	il void upd(int x){
		siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + 1;
	}
	il void dtpath(int x){
		if(!isr(x)){
			dtpath(pa);
		}
		dt(x);
	}
	il void rot(int x){
		int y=fa[x],z=fa[y],c=inr(x);
		(!isr(y)) && (ch[z][inr(y)]=x);
		(ch[y][c]=ch[x][!c]) && (fa[ch[y][c]]=y);
		fa[fa[ch[x][!c]=y]=x]=z;
		upd(y);
	}
	il void splay(int x){
		for(dtpath(x);!isr(x);rot(x))
			if(!isr(pa)) rot(inr(pa)^inr(x)?pa:x);
		upd(x);
	}
	il int access(int x,int newcol=0,int y=0){
		for(;x;upd(x),x=fa[y=x]){
			splay(x);
			siz[x]-=siz[ch[x][1]];
			if(newcol)
				Upd(col[x],-siz[x]),
				Upd(tag[x]=newcol,siz[x]);
			siz[x]+=siz[ch[x][1]=y];
		}
		return y;
	}
}

int info[maxn],Prev[maxn],to[maxn],cnt_e;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }

void dfs(int now,int ff){
	LCT::col[now]=now,LCT::fa[now]=ff,LCT::siz[now]=1;
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff) dfs(to[i],now);
	Upd(now,1);
}

int ask(int u){
	LCT::splay(u);
	return qsum(LCT::col[u])-LCT::siz[LCT::ch[u][0]];
}

int main(){
	scanf("%d%d",&n,&q);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		Node(u,v),Node(v,u);
	}
	dfs(1,0);char s[10];
	for(int i=1;i<=n;i++)
		LCT::access(i,i),LCT::splay(i),LCT::rev[i]^=1;
	for(;q--;){
		scanf("%s",s);
		if(s[0] == 'u'){
			int u;scanf("%d",&u);
			LCT::access(u,++n),LCT::splay(u),LCT::rev[u]^=1;//beroot
		}
		if(s[0] == 'w'){
			int u;scanf("%d",&u);
			printf("%d\n",ask(u));
		}
		if(s[0] == 'c'){
			int u,v;scanf("%d%d",&u,&v);
			printf("%d\n",ask(u)<ask(v)?u:v);
		}
	}
}

相关文章:

  • 2021-06-02
  • 2021-07-08
  • 2021-12-18
  • 2022-12-23
  • 2021-05-24
  • 2022-12-23
  • 2021-11-02
猜你喜欢
  • 2021-08-18
相关资源
相似解决方案