Codeforces Round #441 Div. 1

  A:显然答案与原数的差不会很大。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,ans[1000],u;
int calc(int n)
{
	int s=0;
	while (n) s+=n%10,n/=10;
	return s;
}
signed main()
{
	n=read();
	for (int i=1;i<=min(n,1000);i++)
	{
		int x=n-i;
		if (calc(x)==i) ans[++u]=x;
	}
	cout<<u<<endl;
	for (int i=1;i<=u;i++) cout<<ans[i]<<endl;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:即求不处于最右端的位置中有多少个1,随便维护。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 300010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,a[N],ans;
bool flag[N];
signed main()
{
	n=read();
	for (int i=1;i<=n;i++) a[i]=read();
	cout<<1<<' ';ans=1;int suf=n+1;
	for (int i=1;i<=n;i++)
	{
		flag[a[i]]=1;
		if (suf==a[i]+1)
		{
			suf=a[i];
			while (flag[suf-1]) suf--;
		}
		ans++;cout<<ans-(n-suf+1)<<' ';
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:按位考虑,倒序贪心,必须改(即前缀与后一个串相同且该位较大)的时候才改。全部扫过一遍后可能仍不合法,需要按同样的做法重新扫一遍,并且可以证明扫两遍之后依旧不合法则无解。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define N 100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,m,len,p[N];
bool flag[N],same[N];
vector<int> a[N],pre[N];
bool astb(int i,int j,int x)
{
	if (a[i].size()<=x) return 1;
	if (a[j].size()<=x) return 0;
	if (flag[a[j][x]]!=flag[a[i][x]]) return flag[a[i][x]];
	else return a[i][x]<=a[j][x];
}
void error(){cout<<"No";exit(0);}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
#endif
	n=read(),m=read();
	for (int i=1;i<=n;i++)
	{
		int m=read();len=max(len,m);
		for (int j=0;j<m;j++) pre[i].push_back(p[j]),p[j]=i,a[i].push_back(read());
	}
	for (int t=3;t;t--)
	{
	for (int i=1;i<n;i++) same[i]=1;
	for (int i=0;i<len;i++)
	{
		for (int k=p[i];k>=1;k=pre[k][i])
		if (k<n)
		{
			if (same[k]&&!astb(k,k+1,i))
			{
				flag[a[k][i]]=1;
				if (!astb(k,k+1,i)) error();
			}
			same[k]&=astb(k,k+1,i)&&astb(k+1,k,i);
		}
	}
	}
	for (int i=1;i<n;i++)
	{
		int f=-1;
		for (int j=0;j<min(a[i].size(),a[i+1].size());j++)
		{
			if (a[i][j]!=a[i+1][j])
			{
				if (flag[a[i][j]]==flag[a[i+1][j]]) f=a[i][j]<a[i+1][j];
				else f=flag[a[i][j]];
				break;
			}
		}
		if (f==0) error();
		if (f==-1) if (a[i].size()>a[i+1].size()) error();
	}
	cout<<"Yes"<<endl;
	int cnt=0;for (int i=1;i<=m;i++) if (flag[i]) cnt++;
	cout<<cnt<<endl;
	for (int i=1;i<=m;i++) if (flag[i]) cout<<i<<' ';
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:考虑枚举区间max,显然只要区间内存在一个数在max不为0的一位为1即可。随便预处理一下就完了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,a[N],pre[N][32],suf[N][32],PRE[N],SUF[N];
ll ans;
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<32;j++) pre[i][j]=pre[i-1][j];
		for (int j=0;j<32;j++)
		if (a[i-1]&(1<<j)) pre[i][j]=i-1;
	}
	for (int j=0;j<32;j++) suf[n+1][j]=n+1;
	for (int i=n;i>=1;i--)
	{
		for (int j=0;j<32;j++) suf[i][j]=suf[i+1][j];
		for (int j=0;j<32;j++)
		if (a[i+1]&(1<<j)) suf[i][j]=i+1;
	}
	a[0]=a[n+1]=1000000010;
	for (int i=1;i<=n;i++)
	{
		int j=i-1;
		while (a[j]<a[i]) j=PRE[j];
		PRE[i]=j;
	}
	SUF[n+1]=n+1;
	for (int i=n;i>=1;i--)
	{
		int j=i+1;
		while (a[j]<=a[i]) j=SUF[j];
		SUF[i]=j;
	}


	for (int i=1;i<=n;i++)
	{
		int x=PRE[i],y=SUF[i];int u=PRE[i],v=SUF[i];
		for (int j=0;j<32;j++)
		if (!(a[i]&(1<<j))) u=max(u,pre[i][j]),v=min(v,suf[i][j]);
		ans+=1ll*(SUF[i]-i)*(u-PRE[i])+1ll*(i-u)*(SUF[i]-v); 
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E:显然二分答案。考虑设f[i][0/1]表示第i个由A/B送是否合法,因为此时另一个人的位置并不重要。转移考虑另一个人上次送货是在哪,但这个转移无法实现,因为转移区间并不连续。但容易发现如果顺推过去就是连续的了。st表预处理区间minmax,二分得到转移边界,bit优化转移即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
#define inf 1000000000 
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,p,q,a[N][2],f[N][2],mn[N][18],mx[N][18],LG2[N],tree[N][2];
int query_max(int l,int r)
{
	if (l>r) return 0;
	return max(mx[l][LG2[r-l+1]],mx[r-(1<<LG2[r-l+1])+1][LG2[r-l+1]]);
}
int query_min(int l,int r)
{
	if (l>r) return inf;
	return min(mn[l][LG2[r-l+1]],mn[r-(1<<LG2[r-l+1])+1][LG2[r-l+1]]);
}
void add(int i,int k,int x){while (k<=n+1) tree[k][i]+=x,k+=k&-k;}
int query(int i,int k){int s=0;while (k) s+=tree[k][i],k-=k&-k;return s;}
bool check(int k)
{
	memset(f,0,sizeof(f));
	memset(tree,0,sizeof(tree));
	//cout<<k<<endl;
	f[0][0]=f[0][1]=1;
	for (int i=0;i<=n;i++)
		for (int j=0;j<2;j++)
		{
			if (i) f[i][j]=(query(j,i)>0);
			if (f[i][j])
			{
				int l=i+1,r=n,x=i;
				while (l<=r)
				{
					int mid=l+r>>1;
					if (query_max(i+1,mid)-a[i][j]<=k&&a[i][j]-query_min(i+1,mid)<=k) x=mid,l=mid+1;
					else r=mid-1;
				}
				//cout<<i<<' '<<j<<' '<<x<<' '<<a[i][j]<<' '<<query_max(i+1,x)<<' '<<query_min(i+1,x)<<endl;
				add(j^1,i+1,1),add(j^1,x+1,-1);
			}
		}
	return f[n][0]|f[n][1];
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),a[0][0]=read(),a[0][1]=read();
	for (int i=1;i<=n;i++) mn[i][0]=mx[i][0]=a[i][0]=a[i][1]=read();
	for (int j=1;j<18;j++)
		for (int i=1;i<=n;i++)
		mx[i][j]=max(mx[i][j-1],mx[min(n,i+(1<<j-1))][j-1]),
		mn[i][j]=min(mn[i][j-1],mn[min(n,i+(1<<j-1))][j-1]);
	for (int i=2;i<=n;i++)
	{
		LG2[i]=LG2[i-1];
		if ((2<<LG2[i])<=i) LG2[i]++;
	}
	int l=abs(a[0][0]-a[0][1]),r=inf,ans;
	while (l<=r)
	{
		int mid=l+r>>1;
		if (check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  F:按价值从大到小排序,逐个考虑,显然如果选择某个公主后仍然存在匹配方案,就应该将其选中,因为这至多会使后面的一位公主无法匹配,不选一定不优。

  然后考虑动态判断合法性。显然这是一个二分图匹配问题,但当然不能直接跑。注意到我们需要知道的只是二分图一侧是否存在完美匹配,考虑Hall定理,考虑该侧每个点集在另一侧与几个点相连。由于该侧的点只会与另一侧两个点相连,如果向一个连通块中新加入一个点,不会使得该连通块的任何与其在另一侧有公共点的点集更容易满足定理。所以只要整个连通块是满足Hall定理的,该侧所有点集也一定满足。于是只要用并查集维护二分图每个连通块两侧点的数量差即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,m,ans,fa[N],size[N];
struct data
{
	int x,y,z;
	bool operator <(const data&a) const
	{
		return z>a.z;
	}
}a[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	m=read(),n=read();
	for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].z=read();
	sort(a+1,a+n+1);
	for (int i=1;i<=m;i++) fa[i]=i,size[i]=1;
	for (int i=1;i<=n;i++)
	{
		int p=find(a[i].x),q=find(a[i].y);
		if (p==q)
		{
			if (size[p]>=1) size[p]--,ans+=a[i].z;
		}
		else
		{
			if (size[p]|size[q]) size[p]+=size[q]-1,fa[q]=p,ans+=a[i].z;
		}
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  

原文地址:https://www.cnblogs.com/Gloid/p/10477206.html