相当于求下面这个式子最小
p/x < b/y < p/x-1;
等价于找到一个分数 满足分子,分母最小,(分子最小的话,分母肯定对应的也最小)。且在p/x p/(x-1)之间。
如果不等式左边的上取整t 小于等于 右边的下取整 的话,就说明左边和右边之间有整数,那 y=1,b=t。一定是最小值
如果不存在,我们可以让左边和右边同时减去(t-1),即把左边化成小于1的真分数。然后再左右取倒数。
不等式变成了:(方便书写,我们让t先减1)
(x-1) / ( p- (x-1) * t ) < y / ( b - (t * y) ) < (x)/( p-x*t );
同样的:我们让新的不等式;同样求中间的分数分子分母最小。
然后我们就可以用递归书写这个过程。
复杂度跟辗转相除一样。好强的方法。。
#include
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair pii;
typedef pair pll;
typedef pair pdd;
#define F first
#define S second
const ll INF64=8000000000000000000LL;
const int INF=0x3f3f3f3f;
const ld PI=acos(-1);
const ld eps=1e-9;
const ll MOD=ll(1e9+7);
const int M = 1e5 + 10;
void modup(int&x){if(x>=MOD)x-=MOD;}
//unordered_mapmp;
void gao(ll a,ll b,ll c,ll d,ll &x,ll &y)
{
// printf("%lld")
ll t=(a+b-1)/b;//向上取整
if(c/d>=t)
{
y=1,x=t;
return ;
}
else
{
t--;
gao(d,c-d*t,b,a-b*t,y,x);
x+=t*y;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll p,x;
scanf("%lld%lld",&p,&x);
ll y,b;
gao(p,x,p,x-1,b,y);
ll a=b*x-p*y;
printf("%lld/%lld\n",a,b);
}
return 0;
}
1002
思维+字典树
首先把a,b序列按位建字典树。
有2个对的结论:
1:如果ai与b数组中任意数异或最小的是bj,且bj与a数组中任意数异或最小的是ai。那么ai,bj一定是一组c。
很明显,如果ai,bj不是一对,那么最后我们组成的c一定不是最优的,因为我们只需让ai,bj成一对,那么2对中,这一对变小了,我们把这一对放前面,c的字典序肯定也是整体变小了。
2:ai在b数组中任意数异或最小的是bj,bj在a数组中任意数异或最小的是ak,那么ak在b数组中异或最小的是bl,那么
bl在a数组中异或最小的一定不是ai,只可能是ak或者其他没有在这个关系链上的数。即上述关系只存在2元环,。
证明:ai^bj
由上面2个结论我们就可以做这一题了!!!
直接用栈s维护这个有向图找2元环,找到后删除这2个点,在字典树中同样删除,继续询问其他的点。
遍历a数组如果栈空且a数组未被访问(即删除)加入a数组;
然后执行下面循环直到栈为空:
如果s[top]->B,B->s[top], 那么B和s[top]是一对,删除,top--,但注意了:如果此时的s[top]==B,top要再减一。
因为这就相当于栈顶元素找到了栈顶下面一个元素进行配对。
否则继续找。
注意初始化等细节就没问题了
//KX
#include
using namespace std;
typedef long long ll;
typedef double db;
const int M= 1e5+7;
//结点个数最多30*M
struct Tri
{
int ti[M*31][2],val[M*31],cnt[M*31],sz;
void init()
{
sz=0;
ti[sz][0]=ti[sz][1]=0;
}
void in(int x,int d)
{
int o=0;
for(int i=29;i>=0;i--)
{
int c=(x>>i)&1;
if(!ti[o][c])
{
ti[o][c]=++sz;
cnt[sz]=ti[sz][0]=ti[sz][1]=0;
//初始化
}
o=ti[o][c];
cnt[o]+=d;//删除操作,沿途经过的结点都减去1
}
val[o]=x;
}
int qu(int x)
{
int o=0;
for(int i=29;i>=0;i--)
{
int c=(x>>i)&1;
if(!cnt[ti[o][c]])//当前位指向的相同结点是否存在
c^=1;
o=ti[o][c];
}
return val[o];
}
}tire[2];
int s[M];
int c[M],a[M],b[M];
int main()
{
int t,n;
cin>>t;
while(t--)
{
scanf("%d",&n);
tire[0].init();
tire[1].init();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),tire[0].in(a[i],1);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]),tire[1].in(b[i],1);
int top=0,cnt=0;
for(int i=1;i<=n;i++)
{
if(cnt
1005
dfs搜索+剪枝判断
因为要找第k小字典序,我们很容易想到用dfs求,因为dfs可以保证按照字典序搜索
我们直接设第一个数为100,然后第二个数从100-n+1到100+n-1之间从小到大取,dfs下去,后面的数一样。
如此保证了差分数组字典序第k小,(我们是从前往后,按从小到大深搜)
每搜到一个解复杂度是n^2 第K小的解 复杂度就是K*n^2
#include
using namespace std;
int vis[110];
int p[110];
bool flag;
int n,k;
void dfs(int tmp,int last,int mi,int mx)
{
// printf("%d %d %d %d\n",tmp,last,mi,mx);
if(mx-mi>=n||flag)
return ;
//puts("iok");
if(tmp==n+1)
{
k--;
if(k==0)
{
for(int i=1;i<=n;i++)
printf("%d%c",p[i]-(mi-1)," \n"[i==n]);
flag=true;
}
}
else
{
for(int i=-n+1;i<=n-1;i++)//保证按字典序最小遍历,复杂度最坏也只是K*N^2
{
// puts("ooooo");
int nxt=last+i;
if(!vis[nxt])
{
p[tmp]=nxt;
vis[nxt]=1;
dfs(tmp+1,nxt,min(mi,nxt),max(mx,nxt));
vis[nxt]=0;
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(p,0,sizeof(p));
memset(vis,0,sizeof(vis));
flag=false;
scanf("%d%d",&n,&k);
// for(int i=100;i>=100-n+1;i--)
p[1]=100;
vis[100]=1;
dfs(2,100,100,100);
}
return 0;
}
1007:打表找规律
发现 所有 n 1->n 都满足 a[n]=a[n-1]+a[n-3]这个数组。
然后递推到 任意 i->j 发现结果为a[abs(i-j)+pos],pos为i,j为边界的个数。
1006:扩展KMP裸题。
exnext数组就是题目中的an数组
1004: 大模拟题,就是让n个绝对值消去,有n+1个区间,每个区间对应一个状态,然后直接模拟就行