noip32

T1

暴力很好打,然而我是最后打的,所以只有40pts,其他人都有80pts的说

其实也应该想到的吧

80pts用的 \(set\) ,有个log,所以A不了。

正解:

\(set\) 换成 \(queue\) ,开 \(b\) 个 队列,队首依次塞进对应的素数,每次操作取出队首元素最小的队列,用该队列的队首元素去更新它本身及其之后的队列,在队尾加入乘上该队列所对应的素数即可。

注意,第一次队列里没塞1,直接塞素数,相当于进行了一次操作,所以当操作到第 \(k-1\) 次时,直接输出当前拿出的队列的队首元素即可。

\(queue\) 每次操作都是 \(O(1)\) 的,所以总复杂度 \(O(BK)\) ,可过。

Code
#include<queue>
#include<cstdio>
#define re register
#define int64_t long long
using std::queue;
namespace OMA
{
	int k,b;
	queue<int64_t>q[16];
	int f[16]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
	signed main()
	{
		scanf("%d%d",&b,&k);
		for(re int i=1; i<=b; i++)
		{ q[i].push(f[i]); }
		for(re int i=1,id=1; i<=k-1; i++,id=1)
		{
			for(re int j=2; j<=b; j++)
			{ if(q[j].front()<=q[id].front()){ id = j; } }
			for(re int j=id; j<=b; j++)
			{ q[j].push(1LL*f[j]*q[id].front()); }
			if(i==k-1)
			{ printf("%lld\n",q[id].front()); return 0 ; }
			q[id].pop();
		}
		return 0;
	}
}
signed main()
{ return OMA::main(); }

T2

记搜。

Code
#include<map>
#include<cstdio>
#define a first
#define b second
#define re register
#define int long long
using std::map;
using std::pair;
using std::make_pair;
const int MAX = 1<<6;
typedef pair<int,int>my;
namespace OMA
{
	my d[7];
	int n,cnt,top;
	map<my,int>dp;
	int id[MAX],sum[MAX];
	const int p = 1e9+7;
	inline int dfs(int tmp,my res)
	{
		if(dp[res])
		{ return dp[res]; }
		dp[res] = 1;
		for(re int i=1; i<=top; i++)
		{
			int tot = 0,flag = 0;
			for(re int j=1; j<=top; j++)
			{
				if(!(i&j))
				{ continue ; }
				if((res.a>>j)&1)
				{ tot++; }
				if((res.b>>j)&1)
				{ flag = 1; }
				if(flag||tot>1)
				{ break ; }
			}
			if(flag||tot>1)
			{ continue ; }
			if((res.a>>i)&1)
			{ (dp[res] += sum[i]%p*dfs(tmp+1,make_pair(res.a^(1LL<<i),res.b|(1LL<<i)))%p) %= p; }
			else
			{ (dp[res] += sum[i]%p*dfs(tmp+1,make_pair(res.a|(1LL<<i),res.b))%p) %= p; }
		}
		return dp[res];
	}
	signed main()
	{
		scanf("%lld",&n);
		for(re int i=2; i*i<=n; i++)
		{
			if(n%i==0)
			{
				d[++cnt].a = i;
				while(n%i==0)
				{ d[cnt].b++,n /= i; }
			}
		}
		if(n!=1)
		{ d[++cnt] = make_pair(n,1); }
		for(re int i=1; i<=cnt; i++)
		{ id[1<<i-1] = i; /*printf("%lld %lld\n",d[i].a,d[i].b);*/ }
		top = (1<<cnt)-1,sum[0] = 1;
		for(re int i=1; i<=top; i++)
		{ sum[i] = sum[i^(i&-i)]*d[id[i&-i]].b; /*printf("%lld ",sum[i]);*/ }
		//printf("\n");
		printf("%lld\n",dfs(0,make_pair(0,0))-1);
		return 0;
	}
}
signed main()
{ return OMA::main(); }

T3

看起来比较可做的一道,高斯消元很好想到,方程求解,代入检验。

然而我题读错+不会求 \(\theta\) ,就只拿了30pts。

正解:

按照题目所说的坐标转换,随机找组坐标列两个方程,发现一共有四个未知量,分别为 \(\cos{\theta}\times scale ,\sin{\theta}\times scale,d_{x},d_{y}\) ,所以就再随机找一组坐标来求解,每次求解完,带回检验是否符合要求。

发现,求解出来的是 \(\cos{theta}\times scale,\sin(\theta)\times scale\) ,所以如何求 \(scale\)\(\theta\)

  1. \(scale\) ,根据三角函数相关知识 \(\sin^{2}{\theta}+\cos^{2}{\theta}=1\) 可求得 \(scale\) ,给那俩玩意平方相加开根号即可。

  2. \(\theta\),camth库自带函数 \(acos\) ,记得根据 \(\sin\) 值调整正负。

每回随机找两组,因为要求一半以上,所以找对的概率为 \(\frac{1}{4}\),找错的概率为 \(\frac{3}{4}\),找50组,\(\frac{3}{4}^{50}<10^{-5}\),所以可过。

复杂度 \(O(n)\) ,附带比较大的常数(上界大概为50)。

剩下的就是调试的问题,很exsb,给方程赋值的时候一定不要搞错了,注意统计合法非法时的判断条件,不要混用。

scale记得开根号

附带一系列精美调试信息

Code
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#define MAX 100010
#define re register
namespace OMA
{
	int n,m=4;
	double ar[5][6],tmp[5];
	double x1[MAX],y1[MAX],x2[MAX],y2[MAX];
	const double eps = 1e-6;
	inline double abs(double a)
	{ return a>=0.0?a:-a; }
	inline void swap(double &a,double &b)
	{ double t=a; a=b; b=t; }
	inline void Gauss()
	{
		for(re int i=1; i<=m; i++)
		{
			int k = i;
			for(re int j=i+1; j<=m; j++)
			{
				//if(abs(ar[j][j])>eps&&j<i)
				//{ continue ; }
				if(abs(ar[j][i])>abs(ar[k][i]))
				{ k = j; }
			}
			for(re int j=1; j<=m+1; j++)
			{ swap(ar[i][j],ar[k][j]); }
			//if(abs(ar[i][i])<=eps)
			//{ continue ; }
			for(re int j=1; j<=m; j++)
			{
				if(i!=j)
				{
					double temp = ar[j][i]/ar[i][i];
					for(k = i+1; k<=m+1; k++)
					{ ar[j][k] -= temp*ar[i][k]; }
				}
			}
		}
		for(re int i=1; i<=m; i++)
		{ tmp[i] = ar[i][m+1]/ar[i][i]; }
	}
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1,k=0,a=0,b=0; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while((ch>='0'&&ch<='9')||ch=='.')
			{
				if(ch=='.'){ b = 1; }
				else if(!b)
				{ s = s*10+ch-'0'; }
				else
				{ k = k*10+ch-'0',a++; }
				ch = getchar();
			}
			return s=(pow(0.1,a)*k+s)*w,*this;
		}
	}cin;
	signed main()
	{
		srand(time(NULL));
		cin >> n;
		for(re int i=1; i<=n; i++)
		{ cin >> x1[i] >> y1[i] >> x2[i] >> y2[i]; }
		int cnt = 0;
		while(cnt<=50)
		{
			int p1 = rand()%n+1,p2 = rand()%n+1;
			if(p1==p2)
			{ continue ; }
			cnt++;
			ar[1][1] = x1[p1],ar[1][2] = -y1[p1],ar[1][3] = 1,ar[1][4] = 0,ar[1][5] = x2[p1];
			ar[2][1] = y1[p1],ar[2][2] = x1[p1],ar[2][3] = 0,ar[2][4] = 1,ar[2][5] = y2[p1];
			ar[3][1] = x1[p2],ar[3][2] = -y1[p2],ar[3][3] = 1,ar[3][4] = 0,ar[3][5] = x2[p2];
			ar[4][1] = y1[p2],ar[4][2] = x1[p2],ar[4][3] = 0,ar[4][4] = 1,ar[4][5] = y2[p2];
			Gauss();
			int many = 0;
			for(re int i=1; i<=n; i++)
			{
				if(abs(x1[i]*tmp[1]-y1[i]*tmp[2]+tmp[3]-x2[i])<=eps&&abs(y1[i]*tmp[1]+x1[i]*tmp[2]+tmp[4]-y2[i])<=eps)
				{ many++; }
			}
			//printf("many=%d\n",many);
			if(many>n/2)
			{
				//printf("QAQ\n");
				double scale = sqrt(tmp[1]*tmp[1]+tmp[2]*tmp[2]);
				/*for(re int i=1; i<=m; i++)
				{
					for(re int j=1; j<=m+1; j++)
					{ printf("%0.6lf ",ar[i][j]); }
					printf("\n");
				}*/
				//printf("cos=%0.10lf sin=%0.10lf\n",tmp[1]/scale,tmp[2]/scale);
				printf("%0.10lf\n%0.10lf\n%0.10lf %0.10lf\n",(tmp[2]/scale>=0.0?1.0:-1.0)*acos(tmp[1]/scale),scale,tmp[3],tmp[4]);
				return 0;
			}
		}
		return 0;
	}
}
signed main()
{ return OMA::main(); }

反思总结:

  1. 打暴力不要占用太多时间,但一定要都打上,能优化的地方尽量去优化。
  2. 注意开题顺序,不要死磕一道题。
  3. 注意细节,读题不要出错。真不知道为什么都加粗了我还没看见
posted @ 2021-08-07 21:41  -OMA-  阅读(86)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end