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\) ?
-
\(scale\) ,根据三角函数相关知识 \(\sin^{2}{\theta}+\cos^{2}{\theta}=1\) 可求得 \(scale\) ,给那俩玩意平方相加开根号即可。
-
\(\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(); }
反思总结:
- 打暴力不要占用太多时间,但一定要都打上,能优化的地方尽量去优化。
- 注意开题顺序,不要死磕一道题。
- 注意细节,读题不要出错。
真不知道为什么都加粗了我还没看见