NOI Online 2020 #1

咕了一年的补题……


提高组

冒泡排序

\(x_i=\sum_{j=1}^{i-1}[a_j>a_i]\),则 \(k\) 次冒泡排序相当于将所有 \(x_i\) 变成 \(\max\{0,x_i-k\}\),树状数组维护即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
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^48);c=getchar();}
	return x*f;
}
const int N=2e5+10;
int a[N],p[N],n;
struct bit
{
	int c[N];
	void init(){memset(c,0,sizeof(c));}
	int query(int x,int ans=0){for(;x;x-=x&-x)ans+=c[x];return ans;}
	void modify(int x,int d)
	{
		if(x==0)return;
		for(;x<=n;x+=x&-x)c[x]+=d;
	}
}t1,t2;
signed main()
{
//	freopen("P6186_1.in","r",stdin);
//	freopen("out.txt","w",stdout);
	n=read();int m=read();
	t1.init();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		p[i]=t1.query(n)-t1.query(a[i]);
		t1.modify(a[i],1);
	}
//	for(int i=1;i<=n;i++)printf("%d ",p[i]);
//	puts("");
	t1.init();t2.init();
	for(int i=1;i<=n;i++)t2.modify(p[i],p[i]),t1.modify(p[i],1);
	for(int i=1;i<=m;i++)
	{
		int pos=read();
		if(pos==1)
		{
//			puts("haha");
			int x=read();
			if(a[x]<a[x+1])
			{
				t2.modify(p[x],-p[x]);t2.modify(p[x+1],-p[x+1]);
				t1.modify(p[x],-1);t1.modify(p[x+1],-1);
				swap(a[x],a[x+1]);swap(p[x],p[x+1]);
				p[x+1]++;
				t2.modify(p[x],p[x]);t2.modify(p[x+1],p[x+1]);
				t1.modify(p[x],1);t1.modify(p[x+1],1);
			}
			else
			{
//				puts("t2.modify");
				t2.modify(p[x],-p[x]);t2.modify(p[x+1],-p[x+1]);
//				puts("t1.modify");
				t1.modify(p[x],-1);t1.modify(p[x+1],-1);
				swap(a[x],a[x+1]);swap(p[x],p[x+1]);
				p[x]--;
//				printf("p[x+1]:%d\n",p[x+1]);
//				puts("t2.modify");
				t2.modify(p[x],p[x]);t2.modify(p[x+1],p[x+1]);
//				puts("t1.modify");
				t1.modify(p[x],1);t1.modify(p[x+1],1);
			}
//			puts("HAHA");
		}
		else
		{
//			puts("hehe");
			int k=read();
			if(k>n){puts("0");continue;}
			int cnt=t1.query(n)-t1.query(k),sum=t2.query(n)-t2.query(k);
			printf("%lld\n",sum-k*cnt);
//			puts("HEHE");
		}
	}
	return 0;
}

序列

stO xht Orz

对于操作 \(2\) 连边,形成若干连通块,在和不变的情况下这些连通块内的点的点权能变成任何数。

将上面这些连通块缩点之后再对于操作 \(1\) 连边,如果最后形成的图是二分图,那么在左边右边的差不变的情况下能成为任何数,反之在左边右边差的奇偶性不变的情况下能变成任何数。

#include<bits/stdc++.h>
using namespace std;
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^48);c=getchar();}
	return x*f;
}
const int N=1e5+10,M=2e5+10;
struct bjc
{
	int f[N];
	void init(int n){for(int i=1;i<=n;i++)f[i]=i;}
	int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
	void merge(int x,int y){f[getf(y)]=getf(x);}
}t;
int head[N],ver[M],nxt[M],tot=0;
void add(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot; 
}
struct Edge
{
	int u,v;
	Edge(int uu,int vv){u=uu;v=vv;}
	Edge(){}
}e[M];
int col[N],s[N],d[N];
bool dfs(int x,int c)
{
	s[col[x]=c]+=d[x];bool f=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=ver[i];
		if(~col[y]){if(col[y]==col[x])f=0;continue;}
		if(!dfs(y,c^1))f=0;
	}
	return f;
}
int a[N],b[N];
void sol()
{
	memset(d,0,sizeof(d)); 
	memset(head,0,sizeof(head));tot=0;
	int n=read(),m=read(),cnt=0;
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)b[i]=read();
	t.init(n);
	for(int i=1;i<=m;i++)
	{
		int tt=read(),u=read(),v=read();
		if(tt==2)t.merge(u,v);
		else e[++cnt]=Edge(u,v);
	}
	for(int i=1;i<=n;i++)d[t.getf(i)]+=b[i]-a[i];
	for(int i=1;i<=cnt;i++)
	{
		add(t.getf(e[i].u),t.getf(e[i].v));
		add(t.getf(e[i].v),t.getf(e[i].u));
	}
	memset(col,-1,sizeof(col));
	for(int i=1;i<=n;i++)
	{
		if(~col[i])continue;
		s[0]=s[1]=0;
		bool f=dfs(i,0);
		if(!f&&abs(s[0]%2)!=abs(s[1]%2)){puts("NO");return;}
		if(f&&s[0]!=s[1]){puts("NO");return;}
	}
	puts("YES");
}
int main()
{
	int T=read();
	while(T--)sol();
	return 0;
}

最小环

显然原图必将分出 \(\gcd(n,k)\) 个大小为 \(n/\gcd(n,k)\) 的独立的环。考虑分别处理每一个环,那么一定是尽量将大的与大的乘,可以证明这样是最优的。

对于每个询问都做一遍是 \(\mathcal O(nm)\) 的,不太行,发现 \(\gcd(n,k)\) 最多有 \(\sigma_0(n)\) 个,预处理即可。

时间复杂度 \(\mathcal O(n\sigma_0(n)+m)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
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^48);c=getchar();}
	return x*f;
}
const int N=2e5+10;
int a[N],n,f[N];
int sol(int g)//g:环大小 
{
	deque<int> que;
	int ans=0;
	for(int j=1;j<=n/g;j++)
	{
		while(!que.empty())que.pop_back();
		que.push_back(a[j*g]);
		for(int i=j*g-1;i>=(j-1)*g+2;i--)
		{
			if(que.front()>que.back())ans+=a[i]*que.front(),que.push_front(a[i]);
			else ans+=a[i]*que.back(),que.push_back(a[i]);
		}
		int x=a[(j-1)*g+1];
		ans+=x*que.back()+x*que.front();
	}
	return ans;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
signed main()
{
	n=read();int m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	sort(a+1,a+n+1);
	int ans=0;
	for(int i=1;i<=n;i++)ans+=a[i]*a[i];
	for(int i=1;i*i<=n;i++)
	{
		if(n%i)continue;
		f[i]=sol(i);
		if(i*i!=n)f[n/i]=sol(n/i);
	}
	for(int i=1;i<=m;i++)
	{
		int x=read();
		if(!x)printf("%lld\n",ans);
		else printf("%lld\n",f[n/gcd(n,x)]);
	}
	return 0;
}

普及组

买铅笔

枚举即可。

跑步

反正NOIP不考五边形数

魔法

SF的题解

考虑 \(k=1\) 的情况,设 \(f(x,i,j)\) 表示 \(i\sim j\) 用了 \(k\) 次魔法的最小代价,则:

\[f(1,i,j)=\min_{(u,v,w)\in E}\{f(0,i,u)+f(0,v,j)-w\} \]

接下来考虑 \(k\ge 2\) 的情况,有:

\[f(x,i,j)=\min_{u=1}^n\{f(x-1,i,u)+f(1,u,j)\} \]

一个经典的矩乘式子,建立矩阵跑快速幂即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
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^48);c=getchar();}
	return x*f;
}
const int N=110;
int n;
int e[N][N],f[N][N];
struct mat
{
	int a[N][N];
	mat(){memset(a,0x3f,sizeof(a));}
	void init(){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=e[i][j];}
	mat operator * (const mat &x) const
	{
		mat ans;
		for(int k=1;k<=n;k++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					ans.a[i][j]=min(ans.a[i][j],a[i][k]+x.a[k][j]);
		return ans; 
	}
};
struct Edge
{
	int u,v,w;
	Edge(int uu,int vv,int ww){u=uu;v=vv;w=ww;}
	Edge(){}
}a[2510];
mat qpow(mat a,int n)
{
	mat ans;
	ans.init();
	while(n)
	{
		if(n&1)ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
signed main()
{
//	freopen("P6190_3.in","r",stdin);
	memset(e,0x3f,sizeof(e));
	n=read();int m=read(),k=read();
	for(int i=1;i<=n;i++)e[i][i]=0;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		e[u][v]=min(e[u][v],w);
		a[i]=Edge(u,v,w);
	}
	for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=e[i][j];
	for(int k=1;k<=m;k++)
	{
		int u=a[k].u,v=a[k].v,w=a[k].w;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				f[i][j]=min(f[i][j],e[i][u]+e[v][j]-w);
	}
	mat x;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)x.a[i][j]=f[i][j];
	mat ans=qpow(x,k);
	printf("%lld",k==0?e[1][n]:ans.a[1][n]);
	return 0;
}
posted @ 2021-05-18 14:44  zzt1208  阅读(72)  评论(0编辑  收藏  举报