ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)部分题解

题目链接
A.枚举位数,然后判断一下即可。。。。
ps:好像并不需要二分。。。不过问题不大

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t;
LL n,x;
int get(LL y){
	int cnt=0;
	while(y){
		cnt++;
		y/=10;
	}
	return cnt;
}
LL fac[33];
int main() {
  ios::sync_with_stdio(false);
  //5 6 7 8 9 10 11 12
  fac[0]=1;
  for(int i=1;i<=17;i++){
  	fac[i]=fac[i-1]*10;
  }
  for(cin>>t;t;t--){
  	cin>>n>>x;
  	int num=get(x),sta=1;
  	LL tot=0,ans=0;
  	for(int i=num;;i++){
  		LL dis=(fac[i]-x)*i;
  		if(tot+dis<n){
  			tot+=dis;
  			ans+=(fac[i]-x);
  			x=fac[i];
  		}else{
  			LL l=0,r=fac[i]-x,need=0;
  			while(l<=r){
  				LL mid=l+r>>1;
  				if(mid*i+tot<=n)need=mid,l=mid+1;
  				else r=mid-1;
  			}
  			tot+=need*i;
  			ans+=need;
  			break;
  		}
  	}
  	if(tot<n)sta=0;
  	if(sta)cout<<ans<<'\n';
  	else cout<<-1<<'\n';
  }
 	return 0;
}

C.输入输出题

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t,n,p,k;
int main() {
  ios::sync_with_stdio(false);
  for(cin>>t;t;t--){
  	cin>>k>>p>>n;
  	cout<<1ll*n*max(k-p,0)<<'\n';
  }
 	return 0;
}

D.二进制枚举,加bitset优化。。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t,n,m;
int main() {
  ios::sync_with_stdio(false);
  for(cin>>t;t;t--){
  	cin>>n>>m;
  	bitset<22>vis[n+1];
  	for(int i=1;i<=n;i++)vis[i].reset();
  	for(int i=1;i<=m;i++){
  		int s,t;
  		cin>>s>>t;
  		vis[s].set(t-1);
  	}
  	int ans=n;
  	for(int i=0;i<(1<<n);i++){
  		vector<int>v;
  		bitset<22>g;
  		g.reset();
  		for(int j=0;j<n;j++)g.set(j);
  		int res=0;
  		for(int j=0;j<n;j++){
  			if(1<<j&i){
  				res++;
  				g&=vis[j+1];
  			}
  		}
  		ans=max(ans,res+(int)g.count());
  	}
  	cout<<ans<<'\n';
  }
 	return 0;
}

G.把每个点连的边按权值由大到小排序。
把询问存起来,递增排序,在树上模拟模拟即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t,n,q,a[N];
LL ans;
vector<pair<int,int>>v[N];
int dfs(int x,int y,int l,int r){
	int _r=r;
	//cout<<x<<' '<<l<<' '<<r<<endl;
	for(auto k:v[x])if(k.se!=y){
		int da=k.fi;
		if(l>r)break;
		auto f=upper_bound(a+l,a+r+1,da)-a;
		if(k.se!=y)dfs(k.se,x,f,r);
		r=f-1;
	}
	//cout<<x<<' '<<l<<' '<<r<<endl;
	if(l<=r){
		ans+=1ll*(r-l+1)*x;
	}
}
int main() {
  for(scanf("%d",&t);t;t--){
  	scanf("%d%d",&n,&q);
  	for(int i=1;i<=n;i++)v[i].clear();
  	for(int i=1;i<n;i++){
  		int s,t,w;
  		scanf("%d%d%d",&s,&t,&w);
  		v[s].pb({w,t});
  		v[t].pb({w,s});
  	}
  	for(int i=1;i<=n;i++)
  		sort(v[i].begin(),v[i].end(),[](pair<int,int>x,pair<int,int>y){return x.fi>y.fi;});
  	ans=0;
  	for(int i=1;i<=q;i++){
  		int x;
  		scanf("%d",&a[i]);
  	}
  	sort(a+1,a+1+q);
  	dfs(1,0,1,q);
  	printf("%lld\n",ans);
  }
 	return 0;
}

I.显然要求一组 正反字典序都递减的最大集。
那么先按正着排序,然后把反着的字典序离散化之后跑最长不递增子序列即可。
注意,要求连续的两个必须有且只有一个包含字符m。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int _,n;
int id[N],f[N];
void add(int *t,int x,int y){
	for(;x;x-=x&-x)t[x]=max(t[x],y);
}
int get(int *t,int x){
	int to=0;
	for(;x<=n;x+=x&-x)to=max(t[x],to);
		return to;
}
int t1[N],t2[N],vis[N];
string a[N],b[N],c[N];
int main() {
  ios::sync_with_stdio(false);
  for(cin>>_;_;_--){
  	cin>>n;int st=0;
  	for(int i=0;i<=n;i++)f[i]=0,vis[i]=t1[i]=t2[i]=0;
  	for(int i=1;i<=n;i++){
  		cin>>a[i];
  	}
  	sort(a+1,a+1+n,[](string x,string y){return x>y;});
  	for(int i=1;i<=n;i++){
  		for(auto x:a[i]){
  			vis[i]|=x=='m';
  		}
  		st|=vis[i];
  	}
  	for(int i=1;i<=n;i++){
  		b[i]=a[i];
  		reverse(b[i].begin(),b[i].end());
  		c[i]=b[i];
  	}
  	sort(c+1,c+1+n);
  	int len=unique(c+1,c+1+n)-c-1;
  	for(int i=1;i<=n;i++)id[i]=lower_bound(c+1,c+1+len,b[i])-c;
  	int ans=1;
  	for(int i=1;i<=n;i++){
  		if(vis[i]){
  			f[i]=get(t2,id[i])+1;
  			add(t1,id[i],f[i]);
  		}else{
  			f[i]=get(t1,id[i])+1;
  			add(t2,id[i],f[i]);
  		}
  		ans=max(ans,f[i]);
  	}
  	cout<<ans<<'\n';
  }
 	return 0;
}

L.显然这种题就是要算贡献,即每个数被加了或减了几次。
用单调栈可以很好的维护出来。
具体:枚举上下边界,把这个矩形压成一维,值由每列的最大最小值代替,单调栈维护每个值的作用区间(即这个区间的最值就是自己)即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int t,n;
int a[555][555];
int mn[555],mx[555];
int q[555],h=0;
int l1[555],r1[555],l2[555],r2[555];
void get(){
	for(int i=1;i<=n;i++)l1[i]=r1[i]=l2[i]=r2[i]=0;
	h=0;
	mn[n+1]=0;
	q[++h]=1;
	l1[1]=1;
	for(int i=2;i<=n+1;i++){
		while(h>=1&&mn[q[h]]>=mn[i]){
			r1[q[h]]=i-1;
			--h;
		}
		l1[i]=q[h]+1;
		q[++h]=i;
	}
	h=0;
	q[++h]=1;
	l2[1]=1;
	mx[n+1]=555;
	for(int i=2;i<=n+1;i++){
		while(h>=1&&mx[q[h]]<=mx[i]){
			r2[q[h]]=i-1;
			--h;
		}
		l2[i]=q[h]+1;
		q[++h]=i;
	}	
}
int main() {
  for(scanf("%d",&t);t;t--){
  	scanf("%d",&n);
  	for(int i=1;i<=n;i++){
  		for(int j=1;j<=n;j++){
  			scanf("%d",&a[i][j]);
  		}
  	}
  	LL tot=0;
  	for(int i=1;i<=n;i++){
  		for(int j=1;j<=n;j++)mn[j]=555,mx[j]=0;
  		for(int j=i;j<=n;j++){
  			for(int k=1;k<=n;k++){
  				mn[k]=min(mn[k],a[j][k]);
  				mx[k]=max(mx[k],a[j][k]);
  			}	
  			get();
  			for(int k=1;k<=n;k++){
  				tot-=1ll*mn[k]*(k-l1[k]+1)*(r1[k]-k+1);
  				tot+=1ll*mx[k]*(r2[k]-k+1)*(k-l2[k]+1);
  			}
  		}
  	}
  	printf("%lld\n",tot);
  }
 	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40655981/article/details/106365238