An easy problem HDU-5475(暴力or线段树)

点击这里到杭电,里面都是easy problem

An easy problem

Time Limit: 8000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 3645    Accepted Submission(s): 1334

 

Problem Description

One day, a useless calculator was being built by Kuros. Let's assume that number X is showed on the screen of calculator. At first, X = 1. This calculator only supports two types of operation.
1. multiply X with a number.
2. divide X with a number which was multiplied before.
After each operation, please output the number X modulo M.

 

Input

The first line is an integer T(1≤T≤10 ), indicating the number of test cases.
For each test case, the first line are two integers Q and M. Q is the number of operations and M is described above. (1≤Q≤105,1≤M≤109 )
The next Q lines, each line starts with an integer x indicating the type of operation.
if x is 1, an integer y is given, indicating the number to multiply. (0<y≤109 )
if x is 2, an integer n is given. The calculator will divide the number which is multiplied in the nth operation. (the nth operation must be a type 1 operation.)

It's guaranteed that in type 2 operation, there won't be two same n.

 

Output

For each test case, the first line, please output "Case #x:" and x is the id of the test cases starting from 1.
Then Q lines follow, each line please output an answer showed by the calculator.

 

Sample Input

 

1

10 1000000000

1 2

2 1

1 2

1 10

2 3

2 4

1 6

1 7

1 12

2 7

 

Sample Output

 

Case #1:

2

1

2

20

10

1

6

42

504

84

这道题一点都不easy好吧。。。可能是我的English太垃圾了吧,看了几眼题目,试了一下样例,本以为我以为的就是我以为的,没想到纠结了一个多小时居然是因为把题看错了☹。原来除法操作除的是后面数字做标号的被乘的数

看到这个题的限时5000ms,实话告诉我,你有什么想法呢?暴力!啊哈哈哈哈哈,果然这道题暴力能过得去,不过暴力也是要有技巧的,据说乘法能比除法省上3-5倍的时间 ,我的老天爷,怪不得我用除法会被TLE呢。。。

其实无论是线段树还是暴力,必经之路都是将除法转化为乘法,那么怎么转换呢?

其实这道题的每一个输出结果都是由前面的数据来决定的,就拿简单的样例来说吧

设被操作数存在数组a[]中          第6个结果的由来就是 1*a[1]/a[1]*a[3]*a[4]/a[3]/a[4] 
你会发现分子分母是有重复数字的,而且除号后面的数字只是一个索引并不会被乘法操作
并且题目中说了,除法操作不会有相同的被操作数,这就给我们提供了便利
我们可以开一个bool数组来标记这个数是否会被乘法操作,如果会,就把他留着,不会的话,就把他删掉 ,被除掉的数也同样标记为不会被操作,然后每一个结果都相当于从前面开始,将留下的数累乘起来
仔细想想是不是这个道理

枚举固然简单,幸运的是这道题的限时比较长,算是比较仁慈,我的暴力代码用G++跑了3978ms,C++还是被TLE

而线段树就不一样了,用G++跑了2371ms,C++跑了2402ms,无论是哪个,效率提高了一倍呢!

这要是限时改成3000ms暴力不就GG了吗。

不过线段树这玩意确实挺难的,让人脑阔疼,还是在大佬的指点下才摸出来的,太卑微了。。。Orz

// easy  暴力 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int mm=1e5+5;
int t;
int n,mod;
int op;
ll aa[mm];
int book[mm];
int test=1;

int main()
{
	scanf("%d",&t);
	while(t--){
		memset(book,0,sizeof(book));
		scanf("%d%d",&n,&mod);
		printf("Case #%d:\n",test++);
		ll res=1;
		
		for(int i=1;i<=n;i++){
			scanf("%d%lld",&op,&aa[i]);
			if(op==1){
				book[i]=1;//乘法数字留下 
				res=res*aa[i]%mod;
				printf("%lld\n",res);
			} 
			else {
				res=1;
				book[aa[i]]=0;//除法的数抹掉  
				for(int j=1;j<i;j++)
					if(book[j])//除去某个数  相当于之前没乘过他    
						res=res*aa[j]%mod;
				printf("%lld\n",res);
			}
//			for(int i=1;i<=n;i++)
//				cout<<book[i]<<' ';
//			cout<<endl;
//			for(int i=1;i<=n;i++)
//				cout<<aa[i]<<' '; 
//			printf("\n");
		}
	}
	return 0;
}

还有高级一点的线段树,的确有点NAN

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mm=1e5+5;
#define ll long long

int n,mod;
int book[mm];
ll a[mm];
struct node{
	int l,r;
	ll lazy;
}pp[mm<<2];

void build(int k,int l,int r){
	pp[k].l=l;
	pp[k].r=r;
	pp[k].lazy=1;//初始化  
	if(l==r)return ;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void pd(int k){
	pp[k<<1].lazy=(pp[k].lazy*pp[k<<1].lazy)%mod;
	pp[k<<1|1].lazy=(pp[k].lazy*pp[k<<1|1].lazy)%mod;
	pp[k].lazy=1;
}
void change(int k,int l,int r,ll x){//
	if(pp[k].l>=l&&pp[k].r<=r){
		pp[k].lazy=(pp[k].lazy*x)%mod;//乘以 x 然后mod  
		return ;
	}
	pd(k);//深入 
	int mid=(pp[k].l+pp[k].r)>>1;
	if(l<=mid)change(k<<1,l,r,x);
	if(r>mid)change(k<<1|1,l,r,x);
}

ll query(int k,int pos){//查询区间 [1,pos] 的计算结果  
	if(pp[k].l==pp[k].r)
		return pp[k].lazy%mod;//深入到了这个点  return 
	pd(k);
	int mid=(pp[k].l+pp[k].r)>>1;
	if(pos<=mid)
		return query(k<<1,pos);//
	else return query(k<<1|1,pos); //
}

int main()
{
	int t;
	int test=1;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&mod);
		memset(book,0,sizeof(book));
		build(1,1,n);
		for(int i=1;i<=n;i++){
			int op,x;
			scanf("%d%d",&op,&x);
			if(op==2){
				change(1,x,i-1,a[x]);//乘到他的前面 乘法的地方  
				book[i]=book[x]=1;// 标记这个数被除掉了  
			}
			else a[i]=(ll)x;// 乘法将 x 存入a[] 
		}
		for(int i=1;i<=n;i++)
			if(book[i]==0)
				change(1,i,n,a[i]);//将a[i]乘进去 
		printf("Case #%d:\n",test++); 
		for(int i=1;i<=n;i++)
			printf("%lld\n",query(1,i));//查询区间 [1,i]的计算结果  
	} 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值