xbh的比赛题

T1

这个题目是需要往后考虑的,我考虑的少了一点没有考虑之后的区间,一道某节点不合法就直接输出了

#include<bits/stdc++.h>
using namespace std;
//思路出了一点小差错,需要往后考虑的其实 
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
} 
const int N=1e5+1000;
struct node{
	int id,k;
}a[N];
int n;
//int tot;
int main(){
//	freopen("i.in","r",stdin);
	int t=read();
	while(t--){
		int tot=0;
		int num1=0,num2=0;
		n=read();
		memset(a,0,sizeof a);
		for(int i=1;i<=n;i++){
			int num=read();
//			a[i].k=read();
			char s;
			cin>>s;
			if((s=='W'&&a[tot].id==1)||(s=='B'&&a[tot].id==2)){
				a[tot].k+=num;
			}
			else{
				a[++tot].k=num;
			}
//			cout<<"_____"<<s;
			if(s=='W') {
				a[tot].id=1;
				num1+=num;
			}
			else{
				a[tot].id=2;
				num2+=num;
			}
		}
//		puts("");
//		cout<<"__"<<num1<<"__"<<num2<<endl;
//		cout<<"_______";
		if(num1==0){
			printf("%d\n",num2);
			continue;
		}
		if(num2==0){
			printf("%d\n",num1);
			continue;
		}
		int res=__gcd(num1,num2);
//		cout<<res<<endl;;
		if(res==1){
			puts("1");
			continue;
		}
		num1/=res;
		num2/=res;
//		cout<<"___"<<num1<<"___"<<num2<<endl;
		int lst=a[1].k,now=0;
		int id=a[1].id;
		int ans=0;
		bool pan=false;
		for(int i=2;i<=tot;i++){
//			cout<<i<<"  "<<lst<<"   "<<id<<endl;
			if(a[i].id==id){
				lst+=a[i].k;
				continue;
			}
			if(lst==0){
				lst=a[i].k;
				id=a[i].id;
				continue;
			}
			if(id==1){
				if(lst<num1||lst%num1!=0){
					puts("1");
					pan=true;
//					cout<<"___"<<"75"<<endl;
					break;
				}
				now=num2*(lst/num1);
				if(now>a[i].k){
					puts("1");
//					cout<<"___"<<"82"<<endl;
					pan=true;
					break;
				}
				lst=a[i].k-now;
				ans++;
			}
			if(id==2){
				if(lst<num2||lst%num2!=0){
					puts("1");
					pan=true;
					break;
				}
				now=num1*(lst/num2);
				if(now>a[i].k){
					puts("1");
					pan=true;
					break;
				}
				lst=a[i].k-now;
				ans++;	
			}
			id=a[i].id;
		}
		if(!pan) printf("%d\n",ans);
	}
	return 0;
}

但是数据水的话还是能过不少点的
就会发现缺少一个计数,应该是记录两个颜色的,我只记录了一个
所以我们需要处理出来区间的比值
那个特殊性质特判就行
对于其它情况,发现所有段的比值都等于整个串里面的比值,也就是比值固定。在这个前提下,发现如果对于某一个满足比值区间,其子区间同样满足这个比值,将这个大区间拆成这个子区间和其补集同样满足条件,而且划分出的区间还多了一个。由此得出结论,每一次从边缘贪心地选取出最短可以满足条件的区间一定是最优的。

具体判断时可以计算出当前情况下满足条件需要的数量,检验这个数量是否可以被取到即可。总复杂度 O(n)O(n)O(n)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=1e5+10;
int T,n,cnt1,cnt2,num[MAXN],col[MAXN],ans,c1,c2,t;
char c;
void init() {
	cnt1=cnt2=ans=c1=c2=t=0;
}
signed main() {
	scanf("%lld",&T);
	while(T--) {
		init();
		scanf("%lld",&n);
		for(int i=1; i<=n; i++) {
			scanf("%lld",&num[i]);
			for(c=getchar(); c!='B'&&c!='W'; c=getchar());
			if(c=='B')col[i]=1,cnt1+=num[i];
			else col[i]=2,cnt2+=num[i];
		}
		if(cnt1==0||cnt2==0) {
			printf("%lld\n",cnt1+cnt2);
			continue;
		}
		int GCD=__gcd(cnt1,cnt2);
		cnt1=cnt1/GCD;
		cnt2=cnt2/GCD;
		for(int i=1; i<=n; i++) {
			if(col[i]==1) {
				if(c2%cnt2!=0) {
					c1+=num[i];
					continue;
				}
				t=(cnt1*(c2/cnt2))-c1;
				if(c2&&t>=0&&t<=num[i])ans++,c1=num[i]-t,c2=0;
				else c1+=num[i];
			} else {
				if(c1%cnt1!=0) {
					c2+=num[i];
					continue;
				}
				t=(cnt2*(c1/cnt1))-c2;
				if(c1&&t>=0&&t<=num[i])ans++,c2=num[i]-t,c1=0;
				else c2+=num[i];
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2

这道题可以暴力,直接枚举左右端点然后以最小的数输出就行了
然后发现一个怪点子:打表!
但是就我那个暴力代码,得打上估计1e5∗2s1e5*2 s1e52s这是往小了算,如果数据大,那不得一个点10s10s10s以上,打表就得打一个星期,所以这个思路只适合拿小数据的部分分,这就又不如暴力实在
先看暴力:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MOD = 1e9 + 9;
int n;
int main() {
//    freopen("math.in", "r", stdin);
//    freopen("math.out", "w", stdout);
    scanf("%d", &n);
    for (ll i = 1;; i++) {
        int res = 1;
        for (ll j = 2; j * (j - 1) < 2 * i; j++) {
            if (2 * i % j == 0 && (2 * i / j - j + 1) % 2 == 0) {
                res++;
            }
        }
        if (res == n) {
            cout << i % MOD;
            return 0;
        }
    }
    return 0;
}

能力有限,这能说在梦熊数据下适合我自己水平的正解
然后思考正解:
答案就是求 nnn 的奇因子的个数,然后来思考为什么是:
xxx 加到 yyy 等于 nnn
那么可以推出就是类似的
x=n/k+(k−1)/2x=n/k+(k-1)/2x=n/k+(k1)/2这样可以证明 kkk是奇数的式子。
然后就是求奇因子的个数:
可以把需要用的质数先写出来让,然后分解质因数,给质数上加幂值

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 9;
int power(int u, int v) {
	int ans = 1;
	while (v) {
		if (v & 1)
			ans = 1ll * ans * u % mod;
		u = 1ll * u * u % mod;
		v >>= 1;
	}
	return ans;
}
int prime[40] = { 2,  3,  5,  7,  11, 13, 17, 19, 23, 29,31,  37,  41,  43,  47, 53, 59, 61, 67, 71,73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127};
int tmp[40];
int cnt = 0;
signed main() {
	int n;
	cin >> n;
	int ans = 1;
	for (int i = 2; i * i <= n; i++) {
		while (n % i == 0) {
			tmp[++cnt] = i;
			n /= i; 
		}
	}
	if (n > 1)
		tmp[++cnt] = n;
		
	int cur = 1;
	for (int i = cnt; i >= 1; i--) {
		ans = ans * power(prime[cur], tmp[i] - 1) % mod;
		cur++;
	}
	cout << ans;
	return 0;
}

T3

首先要想出来, 每行最多只能放两个棋子, 这是显然的
于是决策就是一行一行地处理
30分的做法就是裸的枚举,暴搜,枚举这一行放哪里,放几个
然后我想到了压位dpdpdp,按3进制表示当前棋盘的状态,即某一列没有棋子,或者有一个,两个棋子
接着可以发现,棋子的顺序是无所谓的,并不需要准确知道当前棋盘的状态
于是有了 100100100 分做法: dp[i][j][k]d p[i][j][k]dp[i][j][k] 表示放了前 iii 行,有j列是有 111 个棋子,有 kkk 列有两个棋子
转移显然, 分类讨论, 乘法和加法原理, 代码注释中写的很详细

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 101;
const int MOD = 9999973;
int n,m;
ll dp[MAXN][MAXN][MAXN];
inline int C( int num ) { // 相当于C(num,2)
	return num*(num-1)/2;
}
int main() {
	scanf( "%d%d", &n, &m );
	dp[0][0][0] = 1;
	for( int i = 0; i < n; ++i ) // 放第i+1行
		for( int j = 0; j <= m; ++j ) // 有1个棋子的列数
			for( int k = 0; j+k <= m; ++k ) if( dp[i][j][k] ) { // 有2个棋子的列数
					dp[i+1][j][k] = ( dp[i+1][j][k] + dp[i][j][k] ) % MOD; // 不放
					if( m-j-k >= 1 ) dp[i+1][j+1][k] = ( dp[i+1][j+1][k] + dp[i][j][k]*(m-j-k) ) % MOD; // 放一个,在没有棋子的那一列
					if( j >= 1 ) dp[i+1][j-1][k+1] = ( dp[i+1][j-1][k+1] + dp[i][j][k]*j ) % MOD; // 放一个,在有一个棋子的那一列
					if( m-j-k >= 2 ) dp[i+1][j+2][k] = ( dp[i+1][j+2][k] + dp[i][j][k]*C(m-j-k) ) % MOD; // 放两个,都在没有棋子的两列
					if( m-j-k >= 1 && j >= 1 ) dp[i+1][j][k+1] = ( dp[i+1][j][k+1] + dp[i][j][k]*(m-j-k)*j ) % MOD; // 放两个,一个在没有棋子的列,一个在有一个棋子的列
					if( j >= 2 ) dp[i+1][j-2][k+2] = ( dp[i+1][j-2][k+2] + dp[i][j][k]*C(j) ) % MOD; // 两个,在一个棋子的列
				}
	ll ans = 0;
	for( int i = 0; i <= m; ++i ) // 有1个棋子的列
		for( int j = 0; i+j <= m; ++j ) { // 2个棋子的列
			ans = ( ans + dp[n][i][j] ) % MOD;
		}
	printf( "%lld\n", ans );
	return 0;
}

T4

暴力:根据题意写代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=5e5+10;
int h[N];
signed main(){
//	freopen("jump.in","r",stdin);
//	freopen("jump.out","w",stdout);
	int n=read();
	for(int i=1;i<=n;i++){
		h[i]=read();
	}
	int t=read();
	while(t--){
		int ans=0;
		int l=read(),r=read();
		for(int i=l;i<=r;i++){
			for(int j=i+1;j<=r;j++){
				for(int k=2*j-i;k<=r;k++){
					ans=max(ans,h[i]+h[j]+h[k]);
				}
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

考虑正解如何维护线段树:
乍一看似平只能先枚举 a,ba, ba,b 然后算 h2b−a+1h_{2 b-a+1}h2ba+1hrh_{r}hr 的最大值。
我们尝试分析:很多 (a,b)(a, b)(a,b) 是无效的。
ha,hbh_{a}, h_{b}ha,hb 的大小关系进行分类讨论:
ha≥hbh_{a} \geq h_{b}hahb 时枚举 bbb ,我们发现:
如果存在 a1<a2a_{1}<a_{2}a1<a2 满足 ha1,ha2≥hbh_{a_{1}}, h_{a_{2}} \geq h_{b}ha1,ha2hb ,那么 (a1,b)\left(a_{1}, b\right)(a1,b) 是无效的。
因为一组方案 (a1,b,c)\left(a_{1}, b, c\right)(a1,b,c) 一定可以被 (a1,a2,c)\left(a_{1}, a_{2}, c\right)(a1,a2,c) 替代。
也就是说只需找到最大的 aaa 满足 a<b,ha≥hba<b, h_{a} \geq h_{b}a<b,hahb 即可。
ha<hbh_{a}<h_{b}ha<hb 时枚举 aaa ,我们同样考虑 b1,b2b_{1}, b_{2}b1,b2 ,发现一组方案 (a,b2,c)\left(a, b_{2}, c\right)(a,b2,c) 一定可以被 (b1,b2,c)\left(b_{1}, b_{2}, c\right)(b1,b2,c) 替代。

于是只需找到最小的 bbb 满足 a<b,ha<hba<b, h_{a}<h_{b}a<b,ha<hb 即可。都可以用单调栈找到。
现在我们枚举 rrr ,设 xlx_{l}xl 是满足 l=al=al=a2b−a+1≤r2 b-a+1 \leq r2ba+1r(a,b)(a, b)(a,b)ha+hbh_{a}+h_{b}ha+hb 的最大值, yly_{l}yl 是限制 l=al=al=a[l,r][l, r][l,r] 的答案,那每次查询只需计算 yly_{l}ylyry_{r}yr 的最大值。

考虑 r−1r-1r1rrrx,yx, yx,y 的变化: 一些 xxx 会进行单点修改;然后 yiy_{i}yi 会与 xi+hrx_{i}+h_{r}xi+hrmaxmaxmax

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
const ll INF=1e16;
const int N=5e5+10;
int a[N], st[N];
int top, n, m;
vector <pair<int,ll> > G1[N];
vector <pair<int,int> > G2[N];
ll ans[N],val[N];
struct tree {
	ll l,r;
	ll mx,ls;
	ll add,add2;
} tr[N<<2];
void pushup(int p) {
	tr[p].mx=max(tr[p<<1].mx,tr[p<<1 | 1].mx);
	tr[p].ls=max(tr[p<<1].ls,tr[p<<1 | 1].ls);
}
void add(int p,ll add,ll add2) {
	tr[p].add2=max(tr[p].add2,tr[p].add+add2);
	tr[p].add+=add;
	tr[p].ls=max(tr[p].ls,tr[p].mx+add2);
	tr[p].mx+=add;
}
void pushdown(int p) {
	add(p<<1,tr[p].add,tr[p].add2);
	add(p<<1 | 1,tr[p].add,tr[p].add2);
	tr[p].add=tr[p].add2=0;
}
void change(int p,int x,int y,ll v) {
	if(x<=tr[p].l && tr[p].r<=y) {
		add(p,v,max(v,0ll));
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(x<=mid){
		change(p<<1,x,y,v);
	}
	if(y>mid){
		change(p<<1 | 1,x,y,v);
	}
	pushup(p);
}
ll query(int p,int x,int y){
	if(x<=tr[p].l && tr[p].r<=y) {
		return tr[p].ls;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	ll res=0;
	if(x<=mid){
		res=max(res,query(p<<1,x,y));
	}
	if(y>mid){
		res=max(res,query(p<<1 | 1,x,y));
	}
	return res;
}
void build(int p,int l,int r){
	tr[p].l=l;
	tr[p].r=r;
	if(l==r){
		tr[p].mx=tr[p].ls=-INF;
		return;
	}
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1 | 1,mid+1,r);
	pushup(p);
}
signed main() {
	scanf("%d", &n);
	for(int i=1;i<=n;i++){
		val[i]=-INF;
		scanf("%d",&a[i]);
		while(top && (a[i]>=a[st[top]])) {
			int x=st[top];
			int y=i;
			int z=2*y-x;
			if(z<=n){
				G1[z].push_back(make_pair(x,a[x]+a[y]));
			}
			top--;
		}
		if(top){
			int x=st[top];
			int y=i;
			int z=2*y-x;
			if(z<=n){
				G1[z].push_back(make_pair(x,a[x]+a[y]));
			}
		}
		st[++top]=i;
	}
	build(1,1,n);
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int l,r;
		scanf("%d%d",&l, &r);
		G2[r].push_back(make_pair(l,i));
	}
	for(int i=1;i<=n;i++){
		for(auto &p : G1[i]) {
			int pos=p.x;
			ll v=p.y;
			if(val[pos]<v) {
				change(1,pos,pos,v-val[pos]);
				val[pos]=v;
			}
		}
		change(1,1,n,a[i]);
		change(1,1,n,-a[i]);
		for(auto& p : G2[i]) {
			int l=p.x;
			int id=p.y;
			ans[id]=query(1,l,i);
		}
	}
	for(int i=1;i<=m;i++) {
		printf("%lld\n",ans[i]);
	}
	return 0;
}

还要多加练习‘

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值