codeforces 上的小题

CF1305C   

题解:   

我们发现虽然 $n$ 很大,但是模数很小,所以相当于 $n$ 个数对 $m$ 取模后不能有重复数字.   

那么其实这个 $n$ 最大也就是 $m$ ,直接 $O(m^2)$ 暴力算就行了. 

code: 

#include <bits/stdc++.h>   
#define ll long long  
#define N 2004  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
int vis[N],arr[200007],bu[200007];    
int main() 
{ 
	// setIO("input");  
	int i,j,n,mod;
	scanf("%d%d",&n,&mod);          
	for(i=1;i<=n;++i) 
	{   
		scanf("%d",&arr[i]);  
		bu[i]=arr[i];   
		arr[i]%=mod;    
		if(vis[arr[i]]) 
		{
			printf("0\n"); 
			return 0;      
		}
		vis[arr[i]]=1;       
	}        
	int ans=1;
	int tmp=1;      
	for(i=1;i<=n;++i) 
	{
		for(j=i+1;j<=n;++j) 
		{
			if(bu[i]<bu[j]) 
				tmp*=-1;  
			ans=ans*(arr[i]-arr[j]+mod)%mod;   
		}
	}    
	printf("%d\n",(tmp*ans+mod)%mod);  
	return 0;
}

 

CF1310A 

我们发现最优的操作方式一定是从小到大,价格高到价格低来进行操作的.   

而我们每增加 1,就会少一个数,所以只需维护当前数中代价最大的就行.   

然后当碰到下一个数的时候来一个堆的启发式合并就行了. 

code: 

#include <bits/stdc++.h>   
#define N 200007
#define ll long long    
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
int rk[N],a[N],t[N],A[N],id[N];     
map<int,int>mp;  
priority_queue<int>q[N];
ll sum[N];     
int main() 
{ 
	// setIO("input");     
	int i,j,n;        
	scanf("%d",&n);   
	for(i=1;i<=n;++i)   
		scanf("%d",&a[i]),A[i]=a[i],mp[a[i]]=1;     
	for(i=1;i<=n;++i) 
		scanf("%d",&t[i]);       
	sort(A+1,A+1+n); 
	int tmp=unique(A+1,A+1+n)-A-1;            
	for(i=1;i<=n;++i)   
		rk[i]=lower_bound(A+1,A+1+tmp,a[i])-A;                    
	for(i=1;i<=n;++i)     
	{
		q[rk[i]].push(t[i]);         
		sum[rk[i]]+=t[i];  
	} 
	ll ans=0;  
	for(i=1;i<=tmp;++i)  
		id[i]=i;  
	for(i=1;i<=tmp;++i)   
	{
		int cur=A[i];     
		for(j=cur;(j==cur||!mp[j])&&!q[id[i]].empty();++j) 
		{   
			int u=q[id[i]].top(); q[id[i]].pop();       
			ans+=sum[i]-u;       
			sum[i]-=u;    
		}  
		if(!q[id[i]].empty()) 
		{       
			if(q[id[i+1]].size()<q[id[i]].size()) 
			{           
				while(!q[id[i+1]].empty()) 
				{
					q[id[i]].push(q[id[i+1]].top());   
					q[id[i+1]].pop();      
				}
				id[i+1]=id[i];  
			} 
			else
			{
				while(!q[id[i]].empty()) 
				{
					q[id[i+1]].push(q[id[i]].top());   
					q[id[i]].pop();      
				}    
			}
			sum[i+1]+=sum[i];    
		}
	}
	printf("%lld\n",ans);  
	return 0;   
}

  

CF1316C

题解:给定两个多项式,求相乘后第几项不会被质数 $p$ 整除.   

直接硬推的话是推不出来的,因为多项式相乘是卷积运算.   

但是假如说我们能在两个多项式中分别找到第一项不能被 $p$ 整除的 $i,j$ 的话第 $(i+j)$ 项就一定是答案了.   

至于正确性,手画一下多项式卷积的情况就可以得知,在 $(i+j)$ 项的系数模 $p$ 后只有 $a[i] \times b[j]$ 是有贡献的.  

由于题目保证了 $gcd(a[0],a[1],....a[n])=gcd(b[0],b[1],....b[m])=1$,故一定能找到这样的 $i,j$.  

code: 

#include <bits/stdc++.h>   
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int main() 
{ 
	// setIO("input");   
	int i,j,n,m,mod,a1=-1,a2=-1;  
	scanf("%d%d%d",&n,&m,&mod);       
	for(i=0;i<n;++i) 
	{
		int x;   
		scanf("%d",&x);   
		if((x%mod)&&a1==-1) 
			a1=i;   
	}
	for(i=0;i<m;++i) 
	{
		int x; 
		scanf("%d",&x);  
		if((x%mod)&&a2==-1) 
			a2=i;  
	}
	printf("%d\n",a1+a2);  
	return 0; 
}

  

CF1320C 

题解:我们可以一维枚举,另一位用线段树维护(扫描线). 这个套路挺常见的. 

code: 

#include <bits/stdc++.h>  
#define ll long long 
#define N 200007  
#define lson now<<1 
#define rson now<<1|1     
#define MAX 1000000   
#define inf 10000000000000000     
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;        
int A[MAX+2],B[MAX+2];   
struct node {
	int x,y,z;   
}no[N];               
struct data {       
	ll tag,maxx;      
}s[(MAX+1)<<2];          
bool cmp(node a,node b) {  
	return a.x<b.x; 
}     
inline void mark(int now,ll v) 
{
	s[now].maxx+=v;    
	s[now].tag+=v;   
}
inline void pushdown(int now) 
{
	if(s[now].tag) 
	{
		mark(lson,s[now].tag); 
		mark(rson,s[now].tag);  
		s[now].tag=0;  
	}
}
void build(int l,int r,int now) 
{
	if(l==r) 
	{ 
		s[now].maxx=(ll)B[l]?(ll)-B[l]:-inf;    
		return ; 
	}   
	int mid=(l+r)>>1;   
	build(l,mid,lson),build(mid+1,r,rson);    
	s[now].maxx=max(s[lson].maxx,s[rson].maxx);  
}
void update(int l,int r,int now,int L,int R,int v) 
{
	if(l>=L&&r<=R) 
	{
		mark(now,v);  
		return ; 
	}
	pushdown(now);   
	int mid=(l+r)>>1;   
	if(L<=mid)     
		update(l,mid,lson,L,R,v);     
	if(R>mid)  
		update(mid+1,r,rson,L,R,v);   
	s[now].maxx=max(s[lson].maxx,s[rson].maxx);  
}   
int main() 
{ 
	// setIO("input");   
	int i,j,n,m,p;      
	scanf("%d%d%d",&n,&m,&p);    
	for(i=1;i<=n;++i) 
	{
		int x,y;  
		scanf("%d%d",&x,&y);  
		if(!A[x]||A[x]>y) 
			A[x]=y;  
	}   
	for(i=1;i<=m;++i) 
	{
		int x,y; 
		scanf("%d%d",&x,&y);   
		if(!B[x]||B[x]>y) 
			B[x]=y;       
	} 
	for(i=1;i<=p;++i) 
		scanf("%d%d%d",&no[i].x,&no[i].y,&no[i].z);               
	sort(no+1,no+1+p,cmp);         
	build(1,MAX,1);                    
	ll ans=-inf;  
	for(i=j=1;i<=MAX;++i) 
	{                     
		if(A[i]) 
		{             
			for(;j<=p&&no[j].x<i;++j) 
				if(no[j].y<MAX)
					update(1,MAX,1,no[j].y+1,MAX,no[j].z);       
			ans=max(ans,(ll)-A[i]+s[1].maxx);  
		}
	}
	printf("%lld\n",ans);   
	return 0;
}

  

CF1321C

题解:手画一下发现这个东西是不能 $DP$ 的(因为有后效性).   

所以们为了满足没有后效性,就先删字符大的,后删字符小的,整个过程用链表维护即可. 

code: 

#include <bits/stdc++.h>   
#define N 205  
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;    
int n;     
char str[N];   
int a[N],pre[N],nxt[N],vis[N];  
int main() 
{
	// setIO("input");  
	int i,j;         
	scanf("%d%s",&n,str+1);   
	a[0]=a[n+1]=-1;   
	for(i=1;i<=n;++i)   
		a[i]=str[i]-'a',pre[i]=i-1,nxt[i]=i+1;    
	int ans=0;    
	for(i=25;i;--i) 
	{     
		for(int cnt=1;cnt<=100;++cnt) 
		{
			for(j=1;j<=n;++j) 
			{     
				if(!vis[j]&&a[j]==i&&(a[pre[j]]==i-1||a[nxt[j]]==i-1))  
				{
					vis[j]=1;   
					++ans;    
					nxt[pre[j]]=nxt[j];   
					pre[nxt[j]]=pre[j];   
				}
			}
		}
	}    
	printf("%d\n",ans);  
	return 0;
}

  

 

上一篇:DP小小总结


下一篇:线段树算法一些题