2019 Multi-University Training Contest 5

 

补题状况:

题号12345678910  
状态ΟΟ.ΟΟΟΟ...  

1001

题目要求a=b*x mod p,可以转化成a=b*x-p*y    (y为设的变量)

由0<a<b可知

相当于求下面这个式子最小

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 <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> 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_map<int,int>mp;
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<bj^ak<ak^bl.  bl不可能是前面出现的任何点否则就出现小于号的环。只可能是k,如果是k的话最后一个符号取等号就成立了。

 

由上面2个结论我们就可以做这一题了!!!

直接用栈s维护这个有向图找2元环,找到后删除这2个点,在字典树中同样删除,继续询问其他的点。

遍历a数组如果栈空且a数组未被访问(即删除)加入a数组;

然后执行下面循环直到栈为空:

如果s[top]->B,B->s[top], 那么B和s[top]是一对,删除,top--,但注意了:如果此时的s[top]==B,top要再减一。

因为这就相当于栈顶元素找到了栈顶下面一个元素进行配对。

否则继续找。

注意初始化等细节就没问题了

//KX
#include <bits/stdc++.h>
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<n&&tire[0].qu(a[i])==a[i])
			{
				s[++top]=a[i];
				while(cnt<n&&top)
				{
					if(top&1)//top奇数位置存的是A集合里的元素 
					{
						int B=tire[1].qu(s[top]);
						if(tire[0].qu(B)==s[top])//如果成2元环 ,把B,AA在各自字典树中删去 
						{
							c[++cnt]=s[top]^B;
						//	puts("ok");
						//	printf("cnt:%d   %d\n",cnt,c[cnt]);
							tire[0].in(s[top],-1);
							tire[1].in(B,-1);
							top--;
							if(s[top]==B)top--;
						}
						else//否则把B,AA都加进栈 ,
						{
							s[++top]=B;
						}
					}
					else//top是B集合里的元素 
					{
						int A=tire[0].qu(s[top]);
						if(tire[1].qu(A)==s[top])//如果成2元环 
						{
							c[++cnt]=s[top]^A;
							tire[0].in(A,-1);
							tire[1].in(s[top],-1);
							top--;
							if(s[top]==A)top--;
						}
						else//否则把A,BB都加进栈 
						{
							s[++top]=A;
						}
					}
				}
			}
		}
	//	printf("%d====\n",top);
		sort(c+1,c+1+cnt);
		for(int i=1;i<cnt;i++)
		printf("%d ",c[i]);
		printf("%d\n",c[cnt]); 
	}
   	return 0;
}


1005

dfs搜索+剪枝判断

因为要找第k小字典序,我们很容易想到用dfs求,因为dfs可以保证按照字典序搜索

我们直接设第一个数为100,然后第二个数从100-n+1到100+n-1之间从小到大取,dfs下去,后面的数一样。

如此保证了差分数组字典序第k小,(我们是从前往后,按从小到大深搜)

每搜到一个解复杂度是n^2   第K小的解 复杂度就是K*n^2

#include<bits/stdc++.h>
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个区间,每个区间对应一个状态,然后直接模拟就行

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值