2019浙江省赛部分题解

9 篇文章 0 订阅
2 篇文章 0 订阅

B

题解

https://blog.csdn.net/Cassie_zkq/article/details/89607552

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(ll i = a; i < b; ++i)
#define FORE(a,b) for(ll i = a; i <= b; ++i)
typedef long long ll;
typedef pair<ll,ll> pii;
const ll maxn = 1e5+5;

vector<ll> v[maxn];
ll a[maxn],b[maxn];
ll gcd(ll a, ll b) {
    return b == 0? a : gcd(b,a%b);
}
int main() {
    ll T;
    scanf("%lld", &T);
    while(T--) {
        ll n;
        ll x,y;
        for(int i = 1; i <= 100000; ++i)
            v[i].clear();
        scanf("%lld%lld%lld", &n, &x, &y);
        for(ll i = 1; i <= n; ++i) {
            scanf("%lld", &a[i]);
            b[i] = a[i];
            v[a[i]].push_back(i);
        }
        sort(b+1,b+1+n);
        ll tot = unique(b+1,b+1+n)-b-1;
        ll ox = 0, oy = 0;
        for(ll i = 1; i <= n; ++i) {
            ox += i*a[i];
            oy += i*a[i]*a[i];
            // cout << a[i] << endl;
        }
        // cout << ox <<" " << oy << endl;
        ll ans = 0;
        if(x-ox == 0) {
            if(y != oy) {
                puts("0");
                continue;
            } 
            for(ll i = 1; i <= tot; ++i) {
                ll t = v[b[i]].size();
                ans += t*(t-1)/2;
                // v[b[i]].clear();
            }
            cout << ans << endl;
        } else {
            // cout << x-ox <<" "<< y-oy << endl;
            if(gcd(x-ox, y-oy) != x-ox) {
                puts("0");
            }else {
                ll t = (y-oy)/(x-ox);
                // cout << t << endl;
                for(ll i = 1; i<= n; ++i) {
                    ll ai = a[i], aj = t-ai;
                    if(aj == ai) continue;
                    ll j = i-(x-ox)/(aj-ai);
                    if(j <= i) continue;
                    // cout << ai <<" " << aj << " " << j << endl;
                    // if(ai == aj) continue;
                    if(a[j] == aj) ans++;
                }
                cout << ans << endl;
            }
        }
    }
    return 0;
}

C

题解

当一个数出现 > n/2 次,那肯定是Impossible。否则必定有解。

3 2 1 5 4 4 4 4 7
手动模拟这个例子,很快可以得出
3 2 1 5 4 4 4 4 7
1 3 2 4 5 7 x x x
我们发现此时只有4能填,但是这个位置又不能填4,那么就得找一个位置去填这个4,很明显,在这个位置之前的我们按照贪心思路填的是最优的,没有到万不得已绝对不会去改变它,所以我们找此刻位置之后的能填4的,序列变成
3 2 1 5 4 4 4 4 7
1 3 2 4 5 7 x x 4
此时都后面能填的都填了,没有办法了,已经到了万不得已的情况了,就只能动前面的了,贪心的想,肯定是动越靠后越优,并且此时上方和下方都不能是4。所以我们可以取3 2来填,并且这个3 2应该按字典序最小的填(不按字典序也可以,往下看就知道了)。 序列变成
3 2 1 5 4 4 4 4 7
1 4 4 4 5 7 2 3 4
这种情况满足了条件,但并没有满足最小,此时对于4这些位置我们并不是按贪心思路填下去的,所以应该再把4的这些位置重排序一下,序列变成
3 2 1 5 4 4 4 4 7
1 4 4 4 2 3 5 7 4
这便是最优解。

代码

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e5+5;

int cnt[maxn], a[maxn], ans[maxn];
set<int> vis;
vector<int> va[maxn];
vector<int> vb[maxn];
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n;
        vis.clear();
        scanf("%d", &n);
        memset(cnt,0,sizeof cnt);
        bool flag = true;
        for(int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
            cnt[a[i]]++;
            if(cnt[a[i]] > n/2) flag = false;
            vis.insert(a[i]);
            ans[i] = 0;
        }
        if(!flag) {
            puts("Impossible");
            continue;
        }

        vector<int> v1,v2;
        for(int i = 0; i < n; ++i) {
            set<int> :: iterator it = vis.begin();
            
            if(*it == a[i] && vis.size() == 1) {
                for(int j = i+1; j < n; ++j) {
                    if(a[j] != a[i]) {
                        ans[j] = a[i];
                        cnt[a[i]]--;
                    }
                }
                for(int j = i; j < n; ++j) {
                    if(ans[j] == 0 && a[j] == a[i]) {
                        v1.push_back(j);

                    }
                }
                for(int j = i-1; j >= 0; --j) {
                    if(cnt[a[i]] == 0) break;

                    if(ans[j] != a[i] && a[i] != a[j]) {
                        v2.push_back(ans[j]);
                        ans[j] = a[i];
                        cnt[a[i]]--;
                    }
                }
                for(int j = 0; j < v1.size(); ++j) {
                    ans[v1[j]] = v2[j];
                }
                vis.erase(it);
                v1.clear(); v2.clear();
                break;
            } else {
                if(*it == a[i]) {
                    ++it;
                }
                ans[i] = *it;
                cnt[*it]--;
                if(cnt[*it] == 0)
                    vis.erase(it);
            }
        }
        for(int i = 0; i < n; ++i) {
            va[a[i]].push_back(i);
            vb[a[i]].push_back(ans[i]);
        }
        for(int i = 1; i <= n; ++i) {
            if(va[i].size() == 0) continue;
            sort(va[i].begin(), va[i].end());
            sort(vb[i].begin(), vb[i].end());
            for(int j = 0; j < va[i].size(); ++j)
                ans[va[i][j]] = vb[i][j];
            va[i].clear(); vb[i].clear();
        }
        for(int i = 0; i < n; ++i)
            if(i == 0) printf("%d", ans[i]);
            else printf(" %d", ans[i]);
        puts("");
    }
    return 0;
}

E

zoj 4101

题解

从左到右遍历,找出不满足从最左端开始的非降序列的值中的最大值val,以及最先出现的位置pos,对于 < val的值都需要一次操作放到最前面,对于=val的,在pos后面出现的都需要一次操作放到最前面。

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e6+5;

int a[maxn],b[maxn];
int n;
int find(int x) {
	int l = 1, r = n, ans = 0;
	while(l <= r) {
		int mid = (l+r)>>1;
		if(a[mid] <= x) {
			l = mid+1;
			ans = mid;
		} else 
			r = mid-1;
	}
	return ans;
}
int main() {
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			b[i] = a[i];
		}
		int pre = 0;
		int mx = 0;
		int pos = n;
		for(int i = 1; i <= n; ++i) {
			if(a[i] >= pre) {
				pre = a[i];
			} else {
				if(mx < a[i]) {
					mx = a[i];
					pos = i;
				}
			}
		}
		sort(a+1,a+1+n);
		int ct = 0;
		int ans = 0;
		ans = find(mx-1);

		for(int i = pos; i <= n; ++i) {
			if(mx == b[i]) {
				ct++;
			}
		}
		cout << ans + ct << endl;
	}
	return 0;
}

K

zoj 4110

题解

考虑可以经过一次翻转的情况
即s和t中只有一段[l,r]是不相同的,并且这一段经过翻转之后要一模一样,否则就无解。

[l,r]是必须翻转的,但是可以向两边扩散,条件就是 s l − i = = s r + i s_{l-i} == s_{r+i} sli==sr+i

分情况讨论:

  1. s == t,那么对于每个字符其最长的回文子串的半径就是对答案的贡献。采用马拉车可以O(n)算出。
  2. s != t,若可行,那么直接暴力从l,r向两边扩展。

代码

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;

const int maxn=2000010;
char str[maxn];//原字符串
char str2[maxn];
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
    tmp[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
ll MANACHER(char *st,int len)
{
     int mx=0,po=0;
     ll ans=0;//mx即为当前计算回文串最右边字符的最大值
     for(int i=1;i<=len;i++)
     {
         if(mx>i)
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
         else
         Len[i]=1;//如果i>=mx,要从头开始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
         Len[i]++;
         if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
         {
             mx=Len[i]+i;
             po=i;
         }
         ans += Len[i]/2;
     }
     return ans;
 }

int main() {
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%s", str);
		scanf("%s", str2);
		if(strcmp(str,str2) == 0) {
			int len = INIT(str);
			printf("%lld\n", MANACHER(tmp,len));
		} else {
			int l = -1, r = -1;
			int len = strlen(str);
			for(int i = 0; i < len; ++i) {
				if(str[i] != str2[i]) {
					if(l == -1)
						l = i;
					r = i;
				}
			}
			// cout << l << " " << r << endl;
			bool flag = true;
			for(int i = l; i <= r; ++i) {
				// cout << str[i] << " "<< str2[r-i+l] << endl;
				if(str[i] != str2[r-i+l]) {
					flag = false;
					break;
				}
			}
			if(flag == false) {
				puts("0");
				continue;
			}
			int cnt = 1;
			for(int i = l-1, j = r+1; i >= 0 && j < len && str[i] == str[j]; --i, ++j) cnt++;
			printf("%d\n", cnt);
		}

	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值