POI2012题解

#2281. 「POI2012」字母 Letters

对应过来做一遍逆序对就好了。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e9+7;
const int N=30;
int n,a[maxn],now[N];
long long ans;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
string s1,s2;
int pre[N],fir[N],nxt[maxn];
int c[maxn];
int main(){
	n=read();
	cin>>s2>>s1;
	for(int i=1;i<=n;i++){
		int t=s1[i-1]-'A'+1;
		if(!fir[t])fir[t]=pre[t]=i;
		else nxt[pre[t]]=i,pre[t]=i;
	}
	for(int i=1;i<=n;i++){
		int t=s2[i-1]-'A'+1;
		if(!now[t])now[t]=fir[t];
		a[i]=now[t];
		now[t]=nxt[now[t]];
	}
	for(int i=n;i>=1;i--){
		for(int j=a[i];j;j-=(j&(-j)))ans+=c[j];
		for(int j=a[i];j<=n;j+=(j&(-j)))c[j]++;
	}
	printf("%lld
",ans);
	return 0;
}

#2692. 「POI2012」井 Well

没看到只能减不能加,没考虑(0)就半天想不会……

二分答案(mid),前后扫求出不考虑(a_k=0)的答案(tot)

考虑将一个点变为(0)会产生什么影响:过此点作斜率为(pm mid)的两条直线,直线以上的点都受影响。

因为它的波动幅度不超过(mid),故影响范围是一个区间。

维护区间左右端点即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1e9+7;
int n,m,a[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int b[maxn],sum[maxn],cost[maxn];
inline int check(int val){
	int tot=0;
	for(int i=1;i<=n;i++)b[i]=a[i];
	for(int i=2;i<=n;i++)
		if(b[i]>b[i-1]+val){
			tot+=b[i]-b[i-1]-val;
			b[i]=b[i-1]+val;
		}
	for(int i=n-1;i>=1;i--)
		if(b[i]>b[i+1]+val){
			tot+=b[i]-b[i+1]-val;
			b[i]=b[i+1]+val;
		}
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+b[i];
	for(int i=n,l=n;i;i--){
		while(l&&b[l]>(i-l)*val)l--;
		cost[i]=sum[i]-sum[l]-val*(i-l)*(i-l-1)/2;
	}
	for(int i=1,r=1;i<=n;i++){
		while(r<=n&&b[r]>(r-i)*val)r++;
		if(tot+cost[i]+sum[r-1]-sum[i]-val*(r-i)*(r-i-1)/2<=m)
			return i;
	}
	return 0;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	int l=0,r=1<<30,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld %lld
",check(ans),ans);
	return 0;
}

#2689. 「POI2012」节日 Festival

不要以为数据范围大就不能用( ext{floyd}),只要你想不到别的方法,那它就可以。

算是几乎把差分约束有学了一遍吧(也不知道以前我学了个啥)。

连边如下:(add(a_i,b_i,1),add(b_i,a_i,-1),add(d_i,c_i,0))

跑一边( ext{tarjan})缩点,考虑每个强连通分量。

一个( ext{scc})内,显然由于大小关系卡得很死,那么它的取值就是最短路,它的贡献则是直径加一。

出现负环即为不合法,由于我们对每个( ext{scc})( ext{floyd})过了,只需判断(dis[i][i]<0)即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=5e5+10;
const int mod=1e9+7;
const int N=666;
int n,m1,m2,ans;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int beg[N],nex[maxn],to[maxn],e;
inline void add(int x,int y){
	e++;nex[e]=beg[x];
	beg[x]=e;to[e]=y;
}
int dis[N][N],col[N],scc;
int dfn[N],low[N],ti,ins[N];
stack<int>st;
inline void tarjan(int x){
	dfn[x]=low[x]=++ti;
	st.push(x);ins[x]=1;
	for(int i=beg[x];i;i=nex[i]){
		int t=to[i];
		if(!dfn[t]){
			tarjan(t);
			low[x]=min(low[x],low[t]);
		}else if(ins[t])
			low[x]=min(low[x],dfn[t]);
	}
	if(dfn[x]==low[x]){
		col[x]=++scc;
		ins[x]=0;
		while(st.top()!=x){
			int t=st.top();
			st.pop();
			col[t]=scc;
			ins[t]=0;
		}
		st.pop();
	}
}
int main(){
	n=read(),m1=read(),m2=read();
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=n;i++)
		dis[i][i]=0;
	int x,y;
	for(int i=1;i<=m1;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
		dis[x][y]=min(dis[x][y],1);
		dis[y][x]=-1;
	}
	for(int i=1;i<=m2;i++){
		x=read(),y=read();
		add(y,x);
		dis[y][x]=min(dis[y][x],0);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int t=1;t<=scc;t++){
		for(int i=1;i<=n;i++)
			if(col[i]==t)
			for(int j=1;j<=n;j++)
				if(col[j]==t)
				for(int k=1;k<=n;k++)
					if(col[k]==t)
						dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
		int len=-inf;
		for(int i=1;i<=n;i++)
			if(col[i]==t)
			for(int j=1;j<=n;j++)
				if(col[j]==t)len=max(len,dis[i][j]);
		ans+=len+1;
	}
	for(int i=1;i<=n;i++)
		if(dis[i][i]<0)return puts("NIE"),0;
	printf("%d
",ans);
	return 0;
}

#2690. 「POI2012」距离 Distance

容易得出(d(a,b)=f(a)+f(b)-2f(gcd(a,b))),其中(f(i))(i)的质因数个数。

套路:考虑枚举(gcd),更新答案,时间复杂度(Theta(nln n))

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e9+7;
int n,a[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int pri[maxn],tot,flag[maxn];
int f[maxn];
int nxt[maxn],hd[maxn];
int q[maxn];
int ans[maxn],pos[maxn];
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=2;i<maxn;i++){
		if(!flag[i])pri[++tot]=i,f[i]=1;
		for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
			flag[i*pri[j]]=1;f[i*pri[j]]=f[i]+1;
			if(i%pri[j]==0)break;
		}
	}
	/*
	for(int i=1;i<=tot;i++)
		for(int j=pri[i];j<maxn;j+=pri[i])
			f[j]++;
	*/
	for(int i=1;i<=n;i++)
		nxt[i]=hd[a[i]],hd[a[i]]=i;
	for(int i=1;i<=n;i++)
		ans[i]=pos[i]=inf;
	for(int i=1;i<maxn;i++){
		int top=0;
		for(int j=i;j<maxn;j+=i)
			for(int k=hd[j];k;k=nxt[k])
				q[++top]=k;
		if(top<=1)continue;
		int p=q[1];
		for(int j=2;j<=top;j++)
			if(f[a[p]]>f[a[q[j]]]||(f[a[p]]==f[a[q[j]]]&&p>q[j]))p=q[j];
		for(int j=1;j<=top;j++){
			if(q[j]==p)continue;
			int val=f[a[q[j]]]+f[a[p]]-2*f[i];
			if(val<ans[q[j]]||(val==ans[q[j]]&&pos[q[j]]>p))
				ans[q[j]]=val,pos[q[j]]=p;
			if(val<ans[p]||(val==ans[p]&&pos[p]>q[j]))
				ans[p]=val,pos[p]=q[j];
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d
",pos[i]);
	return 0;
}

#2691. 「POI2012」约会 Rendezvous

若两点在同一内向树上,则求(lca),否则先走到环上然后解决。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m,s1[maxn],t1[maxn],tot;
int fa[maxn],rd[maxn];
inline int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
int beg[maxn],nex[maxn],to[maxn],e;
inline void add(int x,int y){
	e++;nex[e]=beg[x];
	beg[x]=e;to[e]=y;
}
int f[maxn][20],dep[maxn],rt[maxn];
int col[maxn],sz[maxn],id[maxn];
inline void dfs(int x,int anc,int root){
	f[x][0]=anc;
	for(int i=1;i<=19;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	dep[x]=dep[anc]+1;
	rt[x]=root;
	for(int i=beg[x];i;i=nex[i])
		if(to[i]!=anc&&col[to[i]]!=col[root])
			dfs(to[i],x,root);
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;i>=0;i--)
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=19;i>=0;i--)
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	return f[x][0];
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		fa[i]=i;
	int x,y;
	for(int i=1;i<=n;i++){
		x=i,y=read();
		if(find(x)!=find(y)){
			add(y,x);
			f[x][0]=y;
			rd[x]++;
			fa[find(x)]=find(y);
		}else tot++,s1[tot]=y,t1[tot]=x;
	}
	for(int i=1;i<=tot;i++){
		x=s1[i],y=t1[i];
		int cnt=0;
		while(x!=y){
			col[x]=i;sz[i]++;
			id[x]=++cnt;x=f[x][0];
		}
		col[y]=i;sz[i]++;
		id[y]=sz[i];
	}
	dep[0]=-1;
	for(int i=1;i<=n;i++)
		if(col[i])dfs(i,0,i);
	//for(int i=1;i<=n;i++)
	//	printf("dep[%d]=%d rt[%d]=%d
",i,dep[i],i,rt[i]);
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		if(find(x)!=find(y)){
			puts("-1 -1");
			continue;
		}
		if(rt[x]==rt[y]){
			int anc=lca(x,y);
			printf("%d %d
",dep[x]-dep[anc],dep[y]-dep[anc]);
			continue;
		}
		int a=dep[x],b=dep[y],c=col[rt[x]];
		int s=(id[rt[y]]-id[rt[x]]+sz[c])%sz[c],t=sz[c]-s;
		//printf("s=%d t=%d
",s,t);
		int p=max(a+s,b),q=max(a,b+t);
		int p1=min(a+s,b),q1=min(a,b+t);
		if(p<q||(p==q&&p1<=q1))printf("%d %d
",a+s,b);
		else printf("%d %d
",a,b+t);
	}
	return 0;
}

#2694. 「POI2012」糖果店 Vouchers

不知道为什么感觉波兰人好喜欢链表啊。

这题就维护每个值取到什么地方了,复杂度(Theta(n ln n)),要开( ext{long long})

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int N=1e6+1;
const int mod=1e9+7;
int n,m,a[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int hd[maxn],vis[maxn],cnt;
int ans[maxn],tot;
signed main(){
	m=read();
	int x,y;
	for(int i=1;i<=m;i++)
		x=read(),a[x]=1;
	for(int i=1;i<N;i++)hd[i]=i;
	n=read();
	for(int i=1;i<=n;i++){
		x=read(),y=0;
		while(hd[x]<N&&y<x){
			if(!vis[hd[x]]){
				vis[hd[x]]=1;y++;
				if(a[hd[x]])ans[++tot]=cnt+y;
			}hd[x]+=x;
		}cnt+=x;
	}
	sort(ans+1,ans+1+tot);
	printf("%lld
",tot);
	for(int i=1;i<=tot;i++)
		printf("%lld
",ans[i]);
	return 0;
}

#2695. 「POI2012」衣帽间 Cloakroom

离线是个好东西。可惜我没想到。

考虑离线,从小到大加入(a_i),维护(f_i),记录凑到(i)的方案的最小值的最大值。

状态转移方程有(f_i=max{f_i,min{f_{i-c},b}})

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e9+7;
int n,m,f[maxn],ans[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
struct node{
	int c,a,b;
	bool operator<(const node &x){
		return a<x.a;
	}
}p[maxn];
struct query{
	int m,k,s,id;
	bool operator<(const query &x){
		return m<x.m;
	}
}q[maxn];
inline void insert(int x){
	for(int i=100000;i>=p[x].c;i--)
		if(~f[i-p[x].c])f[i]=max(f[i],min(f[i-p[x].c],p[x].b));
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		p[i]=(node){read(),read(),read()};
	m=read();
	for(int i=1;i<=m;i++)
		q[i]=(query){read(),read(),read(),i};
	sort(p+1,p+1+n);sort(q+1,q+1+m);
	memset(f,-1,sizeof(f));f[0]=inf;
	for(int i=1,j=1;i<=m;i++){
		while(j<=n&&p[j].a<=q[i].m)insert(j++);
		ans[q[i].id]=(f[q[i].k]>q[i].m+q[i].s);
	}
	for(int i=1;i<=m;i++)
		puts(ans[i]?"TAK":"NIE");
	return 0;
}

#2696. 「POI2012」可怕的诗 A Horrible Poem

容易想到(Theta(qsqrt n))的枚举每一个因子的做法,然而正解是(Theta(qlog n))的。

考虑每次除去一个素因子。还有判断函数可以这样写:

inline void pd(int l,int a,int b){
      return make(a,b-l)==make(a+l,b);
}

这样写就能判断(l)是不是(S[a...b])的周期。

我写完正解之后极限数据在本地要跑( ext{15s}),交上去评测机只跑了( ext{26ms}),我想打人。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=5e5+10;
const int base=29;
const int mod=1e8+9;
int n,m,hs[maxn],pw[maxn];string s;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}int flag[maxn],tot;
inline int make(int l,int r){
	return (hs[r]-1ll*hs[l-1]*pw[r-l+1]%mod+mod)%mod;
}int pri[maxn],p[maxn];
inline bool pd(int l,int a,int b){return make(a,b-l)==make(a+l,b);}
int main(){
	freopen("loj2696.in","r",stdin);
	freopen("loj2696.out","w",stdout);
	n=read();cin>>s;
	for(int i=1;i<=n;++i)
		hs[i]=(1ll*hs[i-1]*base%mod+s[i-1]-'a'+1)%mod;
	pw[0]=1;
	for(int i=1;i<=n;++i)
		pw[i]=1ll*pw[i-1]*base%mod;
	for(int i=2;i<=n;i++){
		if(!flag[i])pri[++tot]=i,p[i]=i;
		for(int j=1;j<=tot&&pri[j]*i<=n;j++){
			flag[i*pri[j]]=1;
			p[i*pri[j]]=pri[j];
			if(i%pri[j]==0)break;
		}
	}
	m=read();
	int x,y,len,z;
	for(int i=1;i<=m;++i){
		x=read(),y=read();len=y-x+1;
		for(int j=len;j>1;j/=p[j])
			if(pd(len/p[j],x,y))len/=p[j];
		printf("%d
",len);
	}
	return 0;
}

#2697. 「POI2012」斐波那契表示法 Fibonacci Representation

玄学贪心求证明!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2e5+10;
const int mod=1e9+7;
const ll N=4e17+10;
ll n,m,f[maxn];
inline ll read(){
	ll x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int divide(int l,int r,ll val){
	int ans=1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(f[mid]<=val)ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
int main(){
	n=read();
	f[1]=f[2]=1ll;
	int t=3;
	for(;(f[t]=f[t-1]+f[t-2])<N;t++);
	while(n--){
		m=read();
		int cnt=0;
		while(m){
			cnt++;int p=divide(1,t,m);
			m=min(m-f[p],f[p+1]-m);
		}
		printf("%d
",cnt);
	}
	return 0;
}

#2693. 「POI2012」比赛路线 Tour de Byteotia

两端点都(>k)的边一定不会被删。

所以先加入所有两端点(>k)的边,再拿剩下的边做一次( ext{Kruskal})即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e6+10;
const int mod=1e9+7;
int n,m,k;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int fa[maxn];
inline int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
int tot,a[maxn],b[maxn];
int u[maxn],v[maxn];
int main(){
	n=read(),m=read(),k=read();
	int x,y;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		if(x>y)swap(x,y);
		u[i]=x;v[i]=y;
		if(x>k)fa[find(x)]=find(y);
	}
	for(int i=1;i<=m;i++){
		x=u[i],y=v[i];
		if(x>k)continue;
		if(find(x)==find(y))
			++tot,a[tot]=x,b[tot]=y;
		else fa[find(x)]=find(y);
	}
	printf("%d
",tot);
	for(int i=1;i<=tot;i++)
		printf("%d %d
",a[i],b[i]);
	return 0;
}

#2698. 「POI2012」超夸克 Squarks

排序。考虑枚举(a_2+a_3)的位置。

注意到(a_2+a_3)前面的一定是(a_1+a_2)(a_1+a_3)(a_1+a_4)……(a_1+a_{pos}),故(a_1)(a_{pos})的值都可以确定。

已经确定的这些数会产生一些和,把这些和从序列中去掉后,剩下的第一个一定是(a_1+a_{pos+1}),故又可以知道(a_{pos+1})的值。

发现比(a_2+a_3)小的只有可能是(a_1+a_2)(a_1+a_n),故此处复杂度(Theta(n)),总理论复杂度(Theta(n^3log n))

实际跑得飞快。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,m,a[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
map<int,int>sta,cnt,pre;
int ans[301][301],tot,b[maxn];
int main(){
	n=read();m=n*(n-1)/2;
	for(int i=1;i<=m;i++)a[i]=read();
	for(int i=1;i<=m;i++)sta[a[i]]++;
	sort(a+1,a+1+m);
	for(int i=3;i<=n;i++){
		int sum=(a[1]+a[2]+a[i])/2,flag=1;
		b[1]=sum-a[i];b[2]=sum-a[2];b[3]=sum-a[1];
		for(int j=4;j<=i;j++)b[j]=a[j-1]-b[1];
		cnt.clear(),pre.clear();
		for(int j=1;j<=i;j++){
			for(int k=1;k<j;k++){
				int x=b[j]+b[k];cnt[x]++;
				if(cnt[x]>sta[x]){flag=0;break;}
			}if(!flag)break;
			pre[a[j]]++;
		}if(!flag)continue;
		for(int j=i+1,pos=i+1;j<=n;j++){
			while(pre[a[pos]]<cnt[a[pos]])
				pre[a[pos++]]++;
			b[j]=a[pos]-b[1];
			for(int k=1;k<j;k++){
				int x=b[k]+b[j];cnt[x]++;
				if(cnt[x]>sta[x]){flag=0;break;}
			}if(!flag)continue;
		}if(!flag)continue;flag=0;
		for(int j=1;j<=n;j++)
			flag|=(ans[tot][j]^b[j]);
		if(flag)tot++;
		for(int j=1;j<=n;j++)
			ans[tot][j]=b[j];
	}
	printf("%d
",tot);
	for(int i=1;i<=tot;i++){
		for(int j=1;j<=n;j++)
			printf("%d ",ans[i][j]);
		puts("");
	}
	return 0;
}

#2700. 「POI2012」工资 Salaries

思路挺妙。我们可以求出每个点最大取到的值,考虑转化问题为每个点有上限,能确定几个点。

从小到大排序,直接处理即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e9+7;
int n,m,a[maxn],rt,vis[maxn],mx[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int beg[maxn],nex[maxn],to[maxn],e;
inline void add(int x,int y){
	e++;nex[e]=beg[x];
	beg[x]=e;to[e]=y;
}
pair<int,int>b[maxn];
inline void dfs(int x,int val){
	if(!a[x])b[++m]=make_pair(val,x);
	for(int i=beg[x];i;i=nex[i])
		dfs(to[i],a[to[i]]?a[to[i]]:mx[val-1]);
}
int main(){
	n=read();
	for(int i=1,x;i<=n;i++){
		x=read();a[i]=read();
		if(x==i)rt=i;else add(x,i);
		vis[a[i]]=1;
	}
	for(int i=1;i<=n;i++)
		mx[i]=vis[i]?mx[i-1]:i;
	dfs(rt,n);
	sort(b+1,b+1+m);
	for(int i=1,j=1,k=0;i<=n;i++){
		if(vis[i]){k++;continue;}
		int cnt=0;
		while(b[j].first==i)cnt++,j++;
		if(cnt==1&&j-1+k==i)
			a[b[j-1].second]=i;
	}
	for(int i=1;i<=n;i++)
		printf("%d
",a[i]);
	return 0;
}

#2703. 「POI2012」仓库式商店 Warehouse Store

直接反悔贪心即可。不会证明。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=3e5+10;
const int mod=1e9+7;
int n,m,ans[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int a[maxn],b[maxn];
priority_queue<pair<int,int> >q;
signed main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)b[i]=read();
	for(int i=1,tot=0,t;i<=n;i++){
		tot+=a[i];
		if(tot>=b[i]){
			m++;tot-=b[i];
			q.push(make_pair(b[i],i));
			continue;
		}
		if(q.empty())continue;
		t=q.top().first;
		if(b[i]<t){
			q.pop();
			q.push(make_pair(b[i],i));
			tot+=t-b[i];
		}
	}
	printf("%lld
",m);
	for(int i=1;i<=m;i++){
		ans[i]=q.top().second;
		q.pop();
	}
	sort(ans+1,ans+1+m);
	for(int i=1;i<=m;i++)
		printf("%lld ",ans[i]);
	return 0;
}

#2704. 「POI2012」前后缀 Prefixuffix

不知道怎么想到的。问题要求将原串划分为( ext{ABCBA})的形式。

考虑( ext{A})的长度为(i)( ext{B})的最大长度为(f_i),易证有(f_ileq f_{i+1}+2),故可以(Theta(n))求出(f)数组。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1e9+9;
const int base=141;
int n,hs[maxn],pw[maxn],f[maxn],ans;
string s;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int make(int r,int len){
	return (hs[r]-hs[r-len]*pw[len]%mod+mod)%mod;
}
inline bool check(int r1,int r2,int len){
	return make(r1,len)==make(r2,len);
}
signed main(){
	n=read();cin>>s;
	for(int i=1;i<=n;i++)
		hs[i]=(hs[i-1]*base+s[i-1]-'a'+1)%mod;
	pw[0]=1;for(int i=1;i<=n;i++)pw[i]=pw[i-1]*base%mod;
	for(int i=n/2;i;i--){
		f[i]=min(f[i+1]+2,n/2-i);
		while(!check(i+f[i],n-i,f[i]))f[i]--;
	}
	for(int i=1;i<=n/2;i++)
		if(check(i,n,i))ans=max(ans,i+f[i]);
	printf("%lld
",ans);
	return 0;
}

#2702. 「POI2012」极简主义的安保 Minimalist Security

考虑一个联通块,设一个点的值为(x),则其他所有点的值都可以用(x)表示。

  1. 若可以解出(x),直接计算贡献即可。

  2. 否则计算(x)的范围,每个点有(0leq xleq p_i),注意到整体是一个一次函数,则极值分别取端点即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
#define inf 1e9
const int maxn=5e5+10;
const int maxm=6e6+10;
const int mod=1e9+7;
int n,m,p[maxn],v[maxn];
ll amx,amn;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int beg[maxn],nex[maxm],to[maxm],w[maxm],e;
inline void add(int x,int y,int z){
	e++;nex[e]=beg[x];
	beg[x]=e;to[e]=y;w[e]=z;
}
int vis[maxn],sta[maxn],top,xv;
struct node{int k,b;}a[maxn];
inline void dfs(int x){
	sta[++top]=x;vis[x]=1;
	for(int i=beg[x];i;i=nex[i]){
		int t=to[i];
		if(!vis[t]){
			a[t].k=-a[x].k;
			a[t].b=w[i]-a[x].b;
			dfs(t);
		}else if(a[x].k+a[t].k==0){
			if(a[x].b+a[t].b!=w[i])puts("NIE"),exit(0);
		}else xv=(w[i]-a[x].b-a[t].b)/(a[x].k+a[t].k);
	}
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		p[i]=read();
	int x,y,z;
	for(int i=1;i<=m;i++){
		x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);
	}
	for(int i=1;i<=n;i++){
		if(vis[i])continue;
		xv=inf,top=0;
		a[i].k=1;dfs(i);
		if(xv!=inf){
			for(int j=1,t;j<=top;j++)
				t=sta[j],v[t]=a[t].k*xv+a[t].b;
			for(int j=1;j<=top;j++)
				for(int k=beg[sta[j]];k;k=nex[k])
					if(v[sta[j]]+v[to[k]]!=w[k])return puts("NIE"),0;
			for(int j=1;j<=top;j++)
				if(v[sta[j]]>p[sta[j]])return puts("NIE"),0;
			for(int j=1,t;j<=top;j++)
				t=sta[j],amn+=p[t]-v[t],amx+=p[t]-v[t];
		}else{
			int l=-inf,r=inf,ksum=0;ll bsum=0;
			for(int j=1;j<=top;j++){
				int t=sta[j];
				ksum-=a[t].k;bsum+=p[t]-a[t].b;
				if(a[t].k==1){
					l=max(l,-a[t].b);
					r=min(r,p[t]-a[t].b);
				}else{
					l=max(l,a[t].b-p[t]);
					r=min(r,a[t].b);
				}
			}
			if(l>r)return puts("NIE"),0;
			if(ksum>=0)amn+=ksum*l+bsum,amx+=ksum*r+bsum;
			else amn+=ksum*r+bsum,amx+=ksum*l+bsum;
		}
	}
	printf("%lld %lld
",amn,amx);
	return 0;
}

#2701. 「POI2012」找平地面 Leveling Ground

太难了,先搁着,以后做。

参考资料:zsy大佬的poi2012题解

原文地址:https://www.cnblogs.com/syzf2222/p/14393911.html