题目链接https://ac.nowcoder.com/acm/contest/11254#question
E - Math
题目
思路
打表找规律,可以看出
①(a,a3)
②(i×i×i,i×i×i×i×i-i) 如:i=2时为:2,8,30
代码
#include <iostream>
#include <algorithm>
#define maxx 100000000
typedef long long ll;
using namespace std;
ll rec[maxx];
ll cnt=1;
void init()
{
rec[cnt]=1;
for(ll i=2; i<=1000000; i++)
{
__int128 x=i;
__int128 y=i*i*i;
while(y<=1000000000000000000)
{
cnt++;
rec[cnt]=y;
__int128 t=y;
y=i*i*y-x;
x=t;
}
}
sort(rec+1,rec+cnt+1);
}
int main()
{
int t;
cin>>t;
init();
ll ans=0;
while(t--)
{
ll x;
cin>>x;
ans=lower_bound(rec+1,rec+cnt+1,x)-rec-1;
if(rec[ans+1]==x)
ans++;
cout<<ans<<"\n";
}
return 0;
}
总结
- 在计算y的时候,不能用ll,否则会溢出,得用__int128,是128位整数类型,但是无法使用cout和cin,得自己写函数
void scan(__int128 &x)//输入
{
x = 0;
int f = 1;
char ch;
if((ch = getchar()) == '-') f = -f;
else x = x*10 + ch-'0';
while((ch = getchar()) >= '0' && ch <= '9')
x = x*10 + ch-'0';
x *= f;
}
void print(__int128 x) //cout
{
if(x < 0)
{
x = -x;
putchar(‘-‘);
}
if(x > 9) print(x/10);
putchar(x%10 + ‘0‘);
}
vector<int> v;
void print()
{
__int128_t x = 1100000000000000L;
__int128_t y = 2200000000000000L;
x*=y; //不能直接给x赋值为2420000000000000000000000000000。。。
while(x)
{
v.push_back(x%10);
x/=10;
}
reverse(v.begin(),v.end());
for(int k=0; k<v.size(); k++)
cout<<v[k];
}
int main()
{
print();
return 0;
}
- lower_bound的使用
使用该函数在指定范围内查找某个目标值x时,最终查找到的结果y>=x,使用了二分查找
J - Counting Triangles
题目
就是给出每个边的颜色0或1,然后找三遍颜色一样的三角形
思路
一直在想着怎么搜索,其实,是一道排列组合的题,害〒▽〒
可以发现不满足条件的三角形一定是有两条边颜色不同的,所以只要把全部的情况-(两条边颜色不同的/2)就可以了…
代码
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e3+5;
namespace GenHelper
{
unsigned z1,z2,z3,z4,b,u;
unsigned get()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
bool read() {
while (!u) u = get();
bool res = u & 1;
u >>= 1; return res;
}
void srand(int x)
{
z1=x;
z2=(~x)^0x233333333U;
z3=x^0x1234598766U;
z4=(~x)+51;
u = 0;
}
}
bool edge[8005][8005];
int main() {
ll n, seed;
cin >> n >> seed;
GenHelper::srand(seed);
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
{
edge[j][i] = edge[i][j] = GenHelper::read();
}
ll ans=n*(n-1)*(n-2)/6;
ll now=0;
for(ri i=0;i<n;i++)
{
now=0;
for(ri j=0;j<n;j++)
{
if(i!=j)
{
if(edge[i][j]) ++now;
}
}
ans-=now*(n-1-now)/2;
}
cout << ans << '\n';
return 0;
}
总结
搜索超时的题,一定有简单的方法解决,而不是改进算法,以前也没有做过排列组合的题,涨知识惹
F - 24dian
题目
n个数字和加减乘除,通过任意组合,得到m
限制:
find all the possible set of cards that has a valid solution but any solution involves fraction(小数) in the calculation
思路
- 必须有小数,但结果是整数,所以n<4的时候,是不可能的,结果为0,所以只需要靠考虑n=4的情况
- 结果要升序排,所以在找答案的时候,循环中要求升序
- dfs的写法,尤其是小数的判断
代码
#include <bits/stdc++.h>
#define maxx 100050
using namespace std;
int n,m,cnt;
vector<double> vd;
vector<int> vi,ans[maxx];
int solve(vector<double> vd)
{
if(vd.size()==1)
{
if(fabs(vd[0]-m)<1e-9)
return 2;
return 0;
}
int x=0;
for(int i=0; i<vd.size(); i++)
{
for(int j=i+1; j<vd.size(); j++)
{
vector<double> tmp;
tmp.clear();//
for(int k=0; k<vd.size(); k++)
{
if(k!=i && k!=j)
tmp.push_back(vd[k]);
}
//+
tmp.push_back(vd[i]+vd[j]);
x=max(x,solve(tmp));
if(x==2)
return 2;
//-,减法不满足交换律,有两种情况
tmp.pop_back();
tmp.push_back(vd[i]-vd[j]);
x=max(x,solve(tmp));
if(x==2)
return 2;
tmp.pop_back();
tmp.push_back(vd[j]-vd[i]);
x=max(x,solve(tmp));
if(x==2)
return 2;
//*
tmp.pop_back();
tmp.push_back(vd[i]*vd[j]);
x=max(x,solve(tmp));
if(x==2)
return 2;
// 必须出现小数,要注意分母不能为0
tmp.pop_back();
if(fabs(vd[i])>1e-9)
{
tmp.push_back(vd[j]/vd[i]);
int f=2;
if(vd[j]/vd[i]-floor(vd[j]/vd[i]+1e-8)>1e-9)
f=1;
x=max(x,min(f,solve(tmp)));
if(x==2)
return 2;
tmp.pop_back();
}
if(fabs(vd[j])>1e-9)
{
tmp.push_back(vd[i]/vd[j]);
int f=2;
if(vd[i]/vd[j]-floor(vd[i]/vd[j]+1e-8)>1e-9)
f=1;
x=max(x,min(f,solve(tmp)));
if(x==2)
return 2;
}
}
}
return x;
}
int main()
{
cin>>n>>m;
if(n<4)
{
cout<<0<<endl;
return 0;
}
for(int i=1; i<=13; i++)
{
for(int j=i; j<=13; j++)
{
for(int k=j; k<=13; k++)
{
for(int l=k; l<=13; l++)
{
vi.clear();
vd.clear();
vi.push_back(i),vd.push_back(i);
vi.push_back(j),vd.push_back(j);
vi.push_back(k),vd.push_back(k);
vi.push_back(l),vd.push_back(l);
if(solve(vd)==1)
ans[cnt++]=vi;
}
}
}
}
cout<<cnt<<endl;
for(int i=0; i<cnt; i++)
{
for(int j=0; j<ans[i].size(); j++)
cout<<ans[i][j]<<' ';
cout<<endl;
}
return 0;
}
总结
- double的比较
相等: f a b s ( v d [ 0 ] − m ) < 1 e − 9 fabs(vd[0]-m) < 1e-9 fabs(vd[0]−m)<1e−9
不为0: f a b s ( v d [ i ] ) > 1 e − 9 fabs(vd[i]) > 1e-9 fabs(vd[i])>1e−9
除数为小数: v d [ j ] / v d [ i ] − f l o o r ( v d [ j ] / v d [ i ] + 1 e − 8 ) > 1 e − 9 vd[j]/vd[i]-floor(vd[j]/vd[i]+1e-8) > 1e-9 vd[j]/vd[i]−floor(vd[j]/vd[i]+1e−8)>1e−9
floor()向下取整,ceil()向上取整
这里要判断两个数相除是否为小数的时候,让x/y和floor(c/y)相减,如果差别不大,就说明是整数
比如:2.3-2=0.3,所以结果是小数
-
状态的转变:return 0表示完全不符合,return 2表示结果是符合的,但没有小数出现,return 1表示结果符合且小数出现,使用max和min函数来判断状态
-
题目本身注意:相减不满足交换律,有两种情况;相除有两种情况,且分母不能为0
B - Black and White
题目
n×m的网格,把每一个网格涂成黑色需要花费c(i,j),问怎么涂花费最小
其中,对于2×2的网格来说,如果有三个是黑色的,那么最后一个没有任何花费
思路
这个题竟然是一个最小生成树的题!
如果已经涂完色,就属于一个连通分量,加上要结果最小。。
不同之处是涂完三个最后一个不用图,通过找规律可得,需要涂n+m-1个方块,然后用prim或者kruscal都可以
不过具体怎么写也是第一次见到。。
分成j>=n 和 j<n,分别表示横向和纵向的最短路径,用dis[n*2]就可以表示
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int cost[N][N];
int dis[2*N],vis[2*N];
ll n,m,a,b,c,d,p;
ll A;
int main()
{
cin>>n>>m>>a>>b>>c>>d>>p;
A=a;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
A=(A*A*b+A*c+d)%p;
cost[i][j]=A;
}
}
for(int i=1;i<=n+m;i++)
{
dis[i]=1e9;
}
dis[1]=0;
ll ans=0;
for(int i=1;i<=n+m;i++)
{
int mn=1e9,pos=0;
for(int j=1;j<=n+m;j++)
{
if(!vis[j]&&dis[j]<mn)
{
mn=dis[j];
pos=j;
}
}
vis[pos]=1;
ans+=dis[pos];
if(pos<=n)
{
for(int j=n+1;j<=n+m;j++)
{
dis[j]=min(dis[j],cost[pos][j-n]);
}
}
else if(pos>n)
{
for(int j=1;j<=n;j++)
{
dis[j]=min(dis[j],cost[j][pos-n]);
}
}
}
printf("%lld",ans);
return 0;
}
/*
3 4 4 5 6 7 50
11 28 45 2
39 46 13 30
37 24 31 48
*/
总结
这就是题解吗,i了i了。。