阿克快乐,阿克了DIV3
比赛连接:https://codeforces.com/contest/1462
这场比赛蛮简单的.
目录
D. Add to Neighbour and Remove
E2. Close Tuples (hard version)
F. The Treasure of The Segments
A. Favorite Sequence
题目大意:
给出一个序列,按照题目顺序左右左右输出序列
题目思路:
水题,按照题意模拟
Code:
ll n,m,p;
ll a[maxn];
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
for(int i=1;i<=n;i++) read(a[i]);
int l = 1,r = n;
while(l<r){
printf("%lld %lld ",a[l],a[r]);
l++;
r--;
}
if(l == r) printf("%lld\n",a[l]);
else printf("\n");
}
return 0;
}
B. Last Year's Substring
题目大意:
给出一个数字字符串,问能否通过至多一次的删除子串操作,使最后字符串变为"2020"
题目思路:
考虑最多一次操作,所以只能删除中间的子串,所以判断一下边界就可以了
Code:
ll n,m,p;
char s[maxn];
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
scanf("%s",s+1);
if(n<4) printf("NO\n");
else{
if(s[1] == '2' && s[2] == '0' && s[3] == '2' && s[4] == '0') printf("YES\n");
else if(s[1] == '2' && s[2] == '0' && s[3] == '2' && s[n] == '0') printf("YES\n");
else if(s[1] == '2' && s[2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
else if(s[1] == '2' && s[n-2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
else if(s[n-3] == '2' && s[n-2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
C. Unique Number
题目大意:
问数位和等于x,并且各位数字互不相同的,最小数字
题目思路:
考虑数字互不相同,所以一共就有2的10次方就可能
2进制枚举一下即可
Code:
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
int ans = 1e9+7;
for(int i=0;i<(1<<10);i++){
int res = 0,temp = 0;
for(int k=0;k<10;k++){
if(i>>k&1){
res += k;
a[++temp] = k;
}
}
if(res == n){
if(a[1] == 0) swap(a[1],a[2]);
int tempx = 0;
for(int k=1;k<=temp;k++) tempx = tempx*10+a[k];
ans = min(ans,tempx);
}
}
printf("%d\n",ans == mod?-1:ans);
}
return 0;
}
D. Add to Neighbour and Remove
题目大意:
抽象一下:给出一个序列,问你最多能划分为多少段,使得每段的和,相等。
最后答案即为:n-段数
题目思路:
有一个朴素的思路,直接考虑最终状态的和,那么这个和必然是总和的因子
每个和都去check一下就好了
此时考虑如果总和很大怎么办?
这里给出一种区间dp的写法,与s无关..,思路也蛮简单的,考虑dp[i][k]代表前i个数分成k段的相等值为多少
然后得出一个n^3的暴力,用map优化掉即可
这就显得我很呆..
Code:
ll n,m,p;
ll num[maxn],sum[maxn];
int dp[3005][3005];
int pre[3005];
unordered_map<ll,int>mp[3005];
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
for(int i=1;i<=n;i++) {
read(num[i]);
sum[i] = sum[i-1]+num[i];
mp[i].clear();
}
int mx = 1;
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
dp[i][k] = -1;
for(int i=1;i<=n;i++){
dp[i][1] = sum[i];
for(int k=2;k<=i;k++){
int op = mp[k-1][sum[i]];
if(op) dp[i][k] = sum[i]-sum[op];
}
for(int k=1;k<=i;k++)
if(~dp[i][k]) mp[k][dp[i][k]+sum[i]] = i;
}
for(int i=n,k=0;i>=1;i--,k++){
if(dp[n][i]!=-1){
printf("%d\n",k);
break;
}
}
}
return 0;
}
E2. Close Tuples (hard version)
题目大意:
给出一个长度为n序列,询问有多少个长度为m的子序列,满足序列最大值-序列最小值<=k
题目思路:
枚举最终答案的最小值,然后进行组合数判断即可
假设当前枚举的最小值为x,那么需要知道[x,x+k]有多少个数:这个操作尺取双指针也可以完成,为了简单写了个树状数组,结果想起来二分也可以..
然后分别对最小值的个数,进行枚举即可
Code:
ll n,m,p;
ll num[maxn];
ll sum[maxn];
int vis[maxn];
ll fac[maxn];
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}return ans;
}
ll cal(int m,int n){
if(m<n) return 0;
return ((fac[m]*qpow(fac[m-n],mod-2))%mod*(qpow(fac[n],mod-2))%mod)%mod;
}
void add(int pos){
while(pos <= n){
sum[pos] ++;
pos += pos&-pos;
}
}
ll GetSum(int pos){
ll ans = 0;
while(pos){
ans += sum[pos];
pos -= pos&-pos;
}return ans;
}
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);read(m);read(p);
for(int i=1;i<=n;i++) read(num[i]);
for(int i=1;i<=n;i++) sum[i] = 0,vis[i] = 0;
for(int i=1;i<=n;i++) vis[num[i]]++;
fac[0] = 1;
for(int i=1;i<=n;i++) fac[i] = (fac[i-1]*i)%mod;
for(int i=1;i<=n;i++) add(num[i]);
ll ans = 0;
for(int i=1;i<=n;i++){
if(vis[i]){
ll temp = GetSum(min(i+p,n)) - GetSum(i-1);
for(int k=1;k<=min(m,vis[i]*1ll);k++){
ans = (ans + (cal(vis[i],k)*cal(temp-vis[i],m-k))%mod)%mod;
}
}
}
printf("%lld\n",ans);
}
return 0;
}
F. The Treasure of The Segments
题目大意:
定义一个线段集是good,当且仅当存在一个线段与其他线段全部相交,问最少删除几条线段使得这个线段集good
题目思路:
很明显的思路,枚举是哪一个线段使得这个线段集good
所以就要求得与该线段相交的有哪些线段,用【总数-与该线段相交的线段数】,即为不相交的线段数
最后对于每一个线段的答案,取一个min就好了
这个题好多种做法,因为坐标太大,直接用主席树动态开点了(懒得离散化)
Code:
/*** keep hungry and calm CoolGuang! ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 2e5+700;
const int mod= 1e9+7;
const int up = 1e9;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
struct node{
int a,b;
bool friend operator<(node a,node b){
if(a.a == b.a) return a.b < b.b;
return a.a < b.a;
}
}q[maxn];
int pre[maxn],suf[maxn];
ll sum[maxn];
int cnt = 0;
struct Tree{
int l,r,w;
}t[maxn*35];
int root[maxn];
void Insert(int &now,int pre,ll l,ll r,ll pos){
t[now = ++cnt] = t[pre];
t[now].w ++;
if(l == r) return ;
ll mid = (l+r)/2;
if(pos <= mid) Insert(t[now].l,t[pre].l,l,mid,pos);
else Insert(t[now].r,t[pre].r,mid+1,r,pos);
}
int Find(int now,ll l,ll r,ll x,ll y){
if(x<=l && y>=r) return t[now].w;
ll mid = (l+r)/2;
int ans = 0;
if(x<=mid) ans += Find(t[now].l,l,mid,x,y);
if(y>mid) ans += Find(t[now].r,mid+1,r,x,y);
return ans;
}
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
for(int i=1;i<=n;i++){
read(q[i].a);
read(q[i].b);
}
sort(q+1,q+1+n);
cnt = 0;
root[0] = 0;
for(int i=1;i<=n;i++){
pre[i] = Find(root[i-1],1,2e9,q[i].a,q[i].b);
Insert(root[i],root[i-1],1,2e9,q[i].b);
}
cnt = 0;
root[n+1] = 0;
for(int i=n;i>=1;i--){
suf[i] = Find(root[i+1],1,2e9,q[i].a,q[i].b);
Insert(root[i],root[i+1],1,2e9,q[i].a);
}
ll ans = INF;
for(int i=1;i<=n;i++)
ans = min(ans,n-1-pre[i]-suf[i]);
printf("%lld\n",ans);
}
return 0;
}