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 s1e5∗2s这是往小了算,如果数据大,那不得一个点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+(k−1)/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}h2b−a+1 到 hrh_{r}hr 的最大值。
我们尝试分析:很多 (a,b)(a, b)(a,b) 是无效的。
对 ha,hbh_{a}, h_{b}ha,hb 的大小关系进行分类讨论:
ha≥hbh_{a} \geq h_{b}ha≥hb 时枚举 bbb ,我们发现:
如果存在 a1<a2a_{1}<a_{2}a1<a2 满足 ha1,ha2≥hbh_{a_{1}}, h_{a_{2}} \geq h_{b}ha1,ha2≥hb ,那么 (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,ha≥hb 即可。
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=a 且 2b−a+1≤r2 b-a+1 \leq r2b−a+1≤r 的 (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}yl 到 yry_{r}yr 的最大值。
考虑 r−1r-1r−1 到 rrr 时 x,yx, yx,y 的变化: 一些 xxx 会进行单点修改;然后 yiy_{i}yi 会与 xi+hrx_{i}+h_{r}xi+hr 取 maxmaxmax。
#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;
}
还要多加练习‘