[四校联考]Easy Problems

简单计数

Description
对于两个\(1\)~\(n\)的排列\(a,b\),定义\(orz(a,b)=max(a_1,b_1)+\)...\(+max(a_n,b_n)\)
你需要求出有多少对\((a,b)\)满足\(orz(a,b)\;\geq\;m\)
Input
一行两个正整数\(n,m\)
Output
一行一个整数表示答案。对\(998244353\)取模。
Sample Input

3 8

Sample Output

18

HINT
\(1\;\leq\;n\;\leq\;50,1\;\leq\;m\;\leq\;2500\)
Solution

假设\(a_i=i\),\(f[i][j][k]\)表示\(b\)中已填了\(1\)$i$,其中有$j$个填在$1$\(i\),满足\(max(a_i,b_i)\;\leq\;i\)\(\sum\;max(a_i,b_i)=k\).

分在第\(i\)个位置填\(i\)、把\(i\)放在\(1\)$i-1,i$放$<i$的数、把$i$放在$1$\(i-1,i\)\(>i\)的数、把\(i\)放在\(i+1\)$n,i$放$<i$的数、把$i$放在$i+1$\(n,i\)\(>i\)的数五种情况考虑:
\(f[i][j][k]=f[i-1][j-1][k-i]\)
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;+f[i-1][j-2][k-i\times2]\times[(i-1)-(j-2)]\)
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;+f[i-1][j-1][k-i]\times[(i-1)-(j-1)]\)
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;+f[i-1][j-1][k-i]\times[(i-1)-(j-1)]\)
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;+f[i-1][j][k]\)
最后\(\times\;n!\)即为答案.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 55
#define M 1901
#define K 998244353ll
using namespace std;
typedef long long ll;
int n,m,mx;
ll f[N][N][M],mul=1ll,ans;
inline ll sqr(int k){
	return (ll)(k*k);
}
inline void Aireen(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		mul=mul*(ll)(i)%K;
	f[0][0][0]=1;
	for(int i=1;i<=n;++i)
		for(int j=0;j<=i;++j)
			for(int k=0;k<M;++k){
				if(j&&k>=i) f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-i])%K;
				if(j>=2&&k>=(i<<1)) f[i][j][k]=(f[i][j][k]+f[i-1][j-2][k-(i<<1)]*sqr(i-j+1)%K)%K;
				if(j&&k>=i) f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-i]*(ll)(i-j)%K)%K;
				if(j&&k>=i) f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-i]*(ll)(i-j)%K)%K;
				f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%K;
			}
	for(int i=m;i<M;++i){
		ans=(ans+f[n][n][i]*mul)%K;
	}
	printf("%lld\n",ans); 
}
int main(){
	freopen("easycount.in","r",stdin);
	freopen("easycount.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

简单几何

Description
给定\(n\)个白点和\(m\)个黑点,你需要求出有多少个相邻点异色的
凸四边形。点的坐标为\([1,10^7]\)中的整数。 保证不存在两个点坐标相同,也不存在三点共线。
Input
第一行两个正整数\(n,m\)
接下来\(n+m\)行每行两个整数\(x,y\)表示坐标。前\(n\)行为白点,后\(m\)行为黑点。
Output
一行一个整数表示答案。
Sample Input

5 5
7612781 7923905
390883 6554981
8816160 3805981
4131888 3855473
4491640 5239819
998553 2850237
5957016 3387077
8511547 701391
8935990 677435
3706758 5650977

Sample Output

18

HINT
\(n,m\;\leq\;400\)
Solution
相邻点异色的四边形个数-相邻点异色的凹四边形个数.
相邻点异色的四边形个数:两白点所在直线的一侧黑点个数\(\times\)另一侧黑点个数.
相邻点异色的凹四边形个数:枚举白点,再枚举两个黑点,此时在图中区域\(1\)的白点都会和它们构成一个凹四边形.枚举白点后,将每个黑点与此白点连线的反向延长线和其他白点与此白点的连线按极角排序,分类讨论用加减法及前缀和求解.(延长线所成角跨不跨越极角为\(0\)的向量)

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 405
#define eps 1e-13
using namespace std;
typedef long long ll;
const double pi=acos(-1);
struct point{
	int x,y;
}a[N],b[N];
struct cnt{
	int s;double w;
}p[N+N]; 
int n,m,sum;ll ans;
inline bool cmp(cnt x,cnt y){
	return x.w<y.w;
}
inline point dec(point x,point y){
	return (point){x.x-y.x,x.y-y.y};
}
inline int mul(point x,point y){
	ll k=(ll)(x.x)*(ll)(y.y)-(ll)(y.x)*(ll)(x.y);
	if(!k) return 0;
	if(k<0ll) return -1;
	return 1;
}
inline void Aireen(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&a[i].x,&a[i].y);
	for(int i=1;i<=m;++i)
		scanf("%d%d",&b[i].x,&b[i].y);
	for(int i=1;i<n;++i)
		for(int j=i+1;j<=n;++j){
			sum=0;
			for(int k=1;k<=m;++k)
				if(mul(dec(b[k],a[i]),dec(a[j],a[i]))>0) ++sum;
			ans+=(ll)(sum*(m-sum));
		}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			p[j].w=atan2(b[j].y-a[i].y,b[j].x-a[i].x);
			if(p[j].w>0.0) p[j].w-=pi;
			else p[j].w+=pi;
		}
		sort(p+1,p+1+m,cmp);
		for(int j=1;j<=m;++j){
			p[j].s=0;
			for(int k=1;k<j;++k)
				if(p[j].w-p[k].w<pi) ++p[j].s;
				else{
					--p[j].s;ans-=(ll)(n-1);
				} 
			for(int k=j+1;k<=m;++k)
				if(p[k].w-p[j].w<pi) --p[j].s;
				else ++p[j].s;
		}
		for(int j=1;j<i;++j){
			p[j+m].s=N;p[j+m].w=atan2(a[j].y-a[i].y,a[j].x-a[i].x);
		}
		for(int j=i+1;j<=n;++j){
			p[j+m-1].s=N;p[j+m-1].w=atan2(a[j].y-a[i].y,a[j].x-a[i].x);
		}
		sort(p+1,p+m+n,cmp);sum=0;
		for(int j=1;j<n+m;++j){
			if(p[j].s==N) ++sum;
			else ans-=(ll)(sum*p[j].s);
		}
	}
	printf("%lld\n",ans);
}
int main(){
	freopen("easygeom.in","r",stdin);
	freopen("easygeom.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

简单01串

Description
给定一个长度为\(n\)\(01\)串以及正整数\(m\)
你可以进行以下两种操作:

  1. \(01\)串某一位取反;
  2. \(01\)串前\(k\;\times\;m\)位取反(\(k\)为整数且\(0<k\;\times\;m\;\leq\;n\)

你需要求出至少要进行多少次操作可以使这个\(01\)串的前\(n-m\)位与后 n-m 位相同。
Input
第一行两个整数\(n,m\)
第二行一个长度为\(n\)\(01\)串。
Output
一行一个整数表示答案。
Sample Input

12 3
010011001101

Sample Output

3

HINT
\(n\;\leq\;300,m\;\leq\;55\)
Solution
题意等价于让\(01\)串变成每\(m\)位一循环所需最少步数.
显然\(O(n\times2^{\sqrt{n}})\)的复杂度可以接受.
分两种情况:
\(1.m\leq\sqrt{n},\)枚举循环节的\(01\)串,\(DP\)出每一段所需最少改变次数,\(f[i][0/1]\)表前第\(i\)段是否取反所需最少步数,不足\(m\)的那一段直接判断.
\(k\)表示不娶反所需改变次数,
\(f[i][0]=min(f[i-1][0],f[i-1][1])+k,\)
\(f[i][1]=min(f[i-1][0]+min(2,(i-1)m),f[i-1][1])+m-k\).
\(2.m>\sqrt{n}\)枚举每一段是否取反,贪心判断\(mod\;m\)相同的位变成相同所需最少改变次数.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 305
using namespace std;
typedef long long ll;
int f[N][2],g[N],a[N],b[N],n,m,sum,ans,cnt;
bool flag;char ch[N];
inline void dfs2(int u,int s,int f){
	if(u<0){
		int cnt=0;
		for(int i=0,k,l=n-n%m;i<m;++i){
			k=0;flag=false;
			for(int j=i;j<n;j+=m){
				if(j>=l) flag=true;
				if(a[j]^g[j/m]) ++k;
			}
			if(flag) cnt+=min(n/m+1-k,k);
			else cnt+=min(n/m-k,k);
		}
		ans=min(ans,cnt+s);
		return;
	}
	g[u/m]=f;
	dfs2(u-m,s,f);
	g[u/m]=f^1;
	dfs2(u-m,s+1,f^1);
}
inline void dfs1(int u){
	if(u==m){
		int cnt=0;
		memset(g,0,sizeof(g));
		for(int l=0,j;l<n;l+=m){
			j=l/m;
			for(int i=l;i<l+m&&i<n;++i)
				if(a[i]!=b[i%m]) ++g[j];
			if(n-l<m){
				cnt=min(n%m-g[j],g[j]);
			} 
			else if(!l){
				f[l][0]=g[j];f[l][1]=m-g[j]+1;
			}
			else{
				f[j][0]=min(f[j-1][0],f[j-1][1])+g[j];
				f[j][1]=min(f[j-1][0]+min(2,l),f[j-1][1])+m-g[j];
			}
		}
		ans=min(ans,min(f[(n-n%m-1)/m][0],f[(n-n%m-1)/m][1])+cnt); 
		return;
	}
	b[u]=0;dfs1(u+1);
	b[u]=1;dfs1(u+1);
}
inline void Aireen(){
	scanf("%d%d",&n,&m);
	scanf("%s",ch);
	for(int i=0;i<n;++i){
		a[i]=ch[i]-'0';
		if(a[i]) ++ans;
	}
	ans=min(ans,n-ans);
	if(m<=17) dfs1(0);
	else dfs2(n-n%m,0,0);
	printf("%d\n",ans);
}
int main(){
	freopen("easy01.in","r",stdin);
	freopen("easy01.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
}

2017-01-26 19:18:31

posted @ 2021-11-25 01:40  Aireen_Ye  阅读(41)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.