C. Min Max Sort(edu142)
题意:给定一个序列,有操作OP,是选择两个数字,其中大的放到末尾,小的放到起点,问最少多少次操作使序列是递增的
思路:我们可以发现,当我们进行其他操作时,最后一次操作必然是对1和n进行操作,那么倒数第二次操作就是将2和n-1进行操作,依次类推,我们假设操作k次,那么1~k和k+1~n-k都会被排序,假设n为6,我们操作的顺序是 3 4,2 5,1 6,我们初始k为n>>1=3,我们对3 4检查时,如果发现3 4符合条件,则k--,代表3 4 这一步不需要操作,然后检查2 5,依次进行,如果有一步x不符合条件,那么直接break,我们可以发现,<=x的数字都得进行操作,那么操作次数k就等于x
diamond:
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve() {
int n;
cin >> n;
vector<int> vis(n + 1);
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
vis[x] = i;
}
int ans = n >> 1;
while (ans && vis[ans] < vis[ans + 1] && vis[n - ans + 1] > vis[n - ans])ans--;
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
C. Tea Tasting(edu143)
题意:给了n杯茶和n个人,每杯茶有ai的茶水,每个人每一轮最多喝bi的茶水,第一轮的喝茶顺序是i喝第i杯,第二轮的顺序是第i个人喝第i-1杯,第三轮第i个人喝第i-2杯茶,这样进行n轮,问n轮后每个人最多喝了多少茶。
思路:我们可以发现第i杯茶,只会被第>=i的人依次喝下去,那么我们可以算出每一杯茶会被哪些人喝,比如第2杯茶只能被2 3 4人喝,那么2 3 4 就记录了一次,他们都喝了一次bi的茶水,那么就记录喝了几次最大值,第五个人无法喝b5,那么就把剩下的这个加到ans5之中,不记录次数,最后的答案$ans[i]=ans[i]+cnt[i]* b[i]$,记录cnt,我们可以发现区间的增加,我们可以用差分,树状数组等结构来解决,这边我用的是差分来解决
diamond:
#include<bits/stdc++.h>
using namespace std;
#define int long long///
void solve() {
int n;
cin>>n;
vector<int>a(n+6),b(n+6);
for (int i = 1; i <=n ; ++i) {
cin>>a[i];
}
for (int i = 1; i <=n ; ++i) {
cin>>b[i];
}
vector<int>sum(n+1,0);
for (int i = 1; i <=n ; ++i) {
sum[i]=sum[i-1]+b[i];
}
vector<int>d(n+2);
vector<int>ans(n+1);
for (int i = 1; i <=n ; ++i) {
int r= lower_bound(sum.begin()+i,sum.end(),a[i]+sum[i-1])-sum.begin();
// cout<<r<<endl;
if(r>i){
d[i]++;
if(r<=n){
d[r]--;
}
}
if(r<=n){
ans[r]+=a[i]-(sum[r-1]-sum[i-1]);
}
}
for (int i = 2; i <=n ; ++i) {
d[i]+=d[i-1];
}
for (int i = 1; i <=n ; ++i) {
ans[i]+=d[i]*b[i];
cout<<ans[i]<<" \n"[i==n];
}
}
int32_t main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while (t--){
solve();
}
}
C. Maximum Set(edu144)
题意:从l到r中选择某些数组成数组,数组之间任意两个数x,y满足$x%y==0||y%x==0$,数组个数最大是多少,可以有多少个方案形成这个大小的数组
思路:任意两个数都可以互相除,那么我们可以这样考虑,x,2x,4x,8x都乘2,那么其实也可以乘3,只要个数等于K就可以,乘>=4就不可以了,因为4=2 2,乘的越大,个数越小,那么题意就是可以变成,可以×几个3,假如个数为6,可以×2个3,那么ans+=1+5 1 + 4 * 2。依次进行累加
diamond:
#include<bits/stdc++.h>
using namespace std;
#define int long long///
const int mod =998244353;
typedef pair<int,int>PII;
const int N=1e5+5,M=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7;
const double eps=1e-8;
typedef long long ll;
ll a,b;
int n,p;
int ksm(int x,int y,int p){
int res=1;
while(y){
if(y&1)res=(ll)res*x%p;
x=(ll)x*x%p;
y>>=1;
}
return res;
}
int c(int a,int b,int p){
if(b>a)return 0;
int res=1;
for(int i=1,j=a;i<=b;++i,--j){
res=(ll)res*j%p;
res=(ll)res*ksm(i,p-2,p)%p;
}
return res;
}
int Lucas(ll a,ll b,int p){
if(a<p&&b<p)return c(a,b,p);
return (ll)c(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
}
void solve() {
int l, r;
cin >> l >> r;
int cnt = 0;
int x = l;
while (x <= r) {
cnt++;
x *= 2;
}
int ans = 0, pp = 1;
int lst = l;
for (int i = cnt - 1; i >= 0; --i) {
int g = ksm(3, i, mod) * ksm(2, cnt - 1 - i, mod);
if (r / g < l) {
continue;
}
int cg = c(cnt, i, mod);
ans = (ans + cg * (r / g - lst + 1)) % mod;
lst = r / g + 1;
}
cout << cnt << ' ' << ans << '\n';
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
D. Triangle Coloring(edu143)
题意:给n/3个三角形,n为6的倍数,将三角形的三个节点涂色,给出每条边的权重,涂色保证黑为n/2个,白为n/2个,假设相邻两个点为不同颜色,这这条边的权值被加到ans之中,求最大的贡献值ans
思路:假设权值为 2 3 4 ,我们把3,4这两个相邻的点涂成同一颜色,另一个点涂成另外一个颜色,每一个三角形都类似,最后我们可以发现,第一个三角形是112,那么第二个三角形可以是221,符合五五开的原则,我们可以发现如果是k个三角形,那么一半是112,另一半是221,所以刚开始ans=C(k,k/2);三角形边有四种大小情况
-
1 1 1,ans=ans * 3
-
1 1 2,ans=ans * 2
-
1 2 3 ,ans=ans
-
2 2 1,ans=ans
diamond:
#include<bits/stdc++.h> using namespace std; #define int long long/// const int mod =998244353; typedef pair<int,int>PII; const int N=1e5+5,M=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7; const double eps=1e-8; typedef long long ll; ll a,b; int n,p; int ksm(int x,int y,int p){ int res=1; while(y){ if(y&1)res=(ll)res*x%p; x=(ll)x*x%p; y>>=1; } return res; } int c(int a,int b,int p){ if(b>a)return 0; int res=1; for(int i=1,j=a;i<=b;++i,--j){ res=(ll)res*j%p; res=(ll)res*ksm(i,p-2,p)%p; } return res; } int Lucas(ll a,ll b,int p){ if(a<p&&b<p)return c(a,b,p); return (ll)c(a%p,b%p,p)*Lucas(a/p,b/p,p)%p; } void solve() { cin>>n; int k=n/3; int ans= c(k,k/2,mod); for (int i = 0; i < n/3; ++i) { int x,y,z; cin>>x>>y>>z; map<int,int>mp; mp[x]++; mp[y]++; mp[z]++; if(mp.size()==1){ ans=(ans*3%mod)%mod; } if(mp.size()==2){ if(mp.begin()->second==2) ans=ans*2%mod; } } cout<<ans<<'\n'; } int32_t main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int t=1; //cin>>t; while (t--){ solve(); } }
C. Set or Decrease(edu120)
题意:给定一个数组,有操作OP,你有两个选择,OP1为把一个数字减少1,OP2 将ai变成aj,问最少进行多少次操作数组的和才能<=k
思路:我们可以发现,我们若进行OP2,必然是将最大的变为最小的,所以我们就可以枚举OP2的次数y,然后O1求出满足k时的OP1的次数x,x+y取min即可
diamond:
#include<bits/stdc++.h> using namespace std; #define int long long #define lowbit(x) ( x & -x ) #define endl '\n' int t; int cnt=1; void solve(){ int n,k; cin>>n>>k; vector<int>a(n); int sum=0; for (int i = 0; i <n ; ++i) { cin>>a[i]; sum+=a[i]; } if(sum<=k){ cout<<"0\n"; return; } if(n==1){ cout<<max(a.back()-k,0ll)<<'\n'; return; } // cout<<sum<<endl; int ans=1e15; sort(a.begin(),a.end(),greater<int>()); int sum1=0; if(sum-1<=k){ cout<<"1\n"; return; } if(sum-*a.begin()+a.back()<=k){ cout<<"1\n"; return; } for (int i = 0; i <n-1 ; ++i) { sum1+=a[i]; // cout<<sum1<<"gg"; int y=i+1; int x=(sum-sum1-k+y*a.back()+y)/(y+1); // cout<<y<<' '<<x<<endl; if(x<0)x=0; // cout<<y<<' '<<x<<endl; // x--; // if(sum-sum1+y*a.back()-y*x<=k) ans=min(y+x,ans); } cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>t; for (int i = 1; i <=t ; ++i) { cnt++; solve(); } }
C. Sum on Subarrays(edu145)
题意:给整数n和k,构造一个n大小的数组,使得k个连续子数组的和为整数,其他都为负数
思路:我们可以这样构造,前面都是2,截至点往后都构造为最小-1000,这样可以保证如果前x个是2,那么可以形成$x * (x+1)/2$个区间,所以我们可以枚举找到第一个ans>=k的x,然后将1到x-1变为2,然后再计算k-x缺几个和为整的,我们可以这样想,2 2 2 m,m为1,则可再形成4个,如果m等于-1,那么可再形成3个,如果m=-3,则可以再形成两个,如果m=-5,则可以再形成1个,我们可以O(1)求出m的值,然后后面的数字全部填成 -1000即可
diamond:
#include<bits/stdc++.h> using namespace std; const double g=9.8; struct complex1{ int a,b; }; void solve(){ int n,k; cin>>n>>k; if(!k){ for (int i = 0; i < n; ++i) { cout<<"-1 "; } cout<<"\n"; return; } int cnt=0; for (int i = 1; i <=n ; ++i) { int d=(i*(i+1)/2); if(d>=k){ cnt=i; break; } } vector<int>a(n+1); for (int i = 1; i <cnt ; ++i) { a[i]=2; } if(cnt*(cnt+1)/2==k)a[cnt]=2; else{ int cs=cnt*(cnt+1)/2-k-1; a[cnt]=-1-2*cs; } for (int i = cnt+1; i <=n ; ++i) { a[i]=-1000; } for (int i = 1; i <=n ; ++i) { cout<<a[i]<<' '; } cout<<'\n'; } int main(){ int t=1; cin>>t; while(t--){ solve(); } }
D. Binary String Sorting(edu145)
题意:给定一个01串,有两种操作,OP1是将相邻两个数交换,代价是1e12,OP2是将这个数字删除,代价是1e12+1,问将串变成非递减的串,代价最少是多少
思路:观察串000101001,我们对第一个10交换之后,由于后面还是有0,要么我们就把这些0换到1之前,要么就删除,由于后面的0的位置与1的位置差是大于2的,那么次数就大于2,那么我们就直接删除更优,前缀的1同理,再与不移动的都删除相比,取更小的。那么其实交换最多就只能交换一个位置,其他位置不符合就删除,枚举一下交换的位置即可
diamond:
#include<bits/stdc++.h> using namespace std; const double g=9.8; #define int long long struct complex1 { int a, b; }; const int a=1e12,b=1e12+1; void solve() { string s; cin>>s; s=" "+s; int n=s.size()-1; s=s+" "; vector<int>pre1(n+1),suf0(n+2); for (int i = 1; i <=n ; ++i) { pre1[i]=pre1[i-1]+(s[i]=='1'); } for (int i = n; i >=0 ; --i) { suf0[i]=suf0[i+1]+(s[i]=='0'); } int ans=1e18; for (int i = 1; i <=n ; ++i) { ans=min(ans,(pre1[i-1]+suf0[i+1])*b); } int shan1=0,shan0=0; int x=0,y=0; for (int i = 1; i <=n ; ++i) { if(s[i]=='1'&&s[i+1]=='0'){ ans=min(ans,a+(pre1[i-1]+suf0[i+2])*b); } } cout<<ans<<'\n'; } signed main() { int t = 1; cin >> t; while (t--) { solve(); } }
C. Yet Another Tournament(edu141)
题意:你去参加锦标赛,有n个对手编号1~n,每个人可以战胜编号在他前面的所有选手,如果你要战胜他们,那么需要准备时间,战胜每个人的你需要的准备时间是ai,你一共可以准备m时间,按照胜场排名,问你最低可以排到第几名呢?
思路:按照ai排序,然后你拿下前j个,如果前j个之中有第j号选手,那么你就可以战胜j个选手,那么ans=n-j+1,我们是按0~n-1,同理注意一下就好,如果没有第j号选手,那么我们不选择拿下排序后的j-1选手,然后恢复体力判断是否可以拿下第j位选手,如果拿下,胜场相同,ans还是n-j+1,如果拿不下,排名ans则位n-j+1+1,前缀和二分或者On求出j即可
diamond:
#include<bits/stdc++.h> using namespace std; #define int long long struct complex1 { int a, b; }; int k=0; const int a=1e12,b=1e12+1; void solve() { int n,m; cin>>n>>m; vector< int >g(n); vector<pair<int,int>>gg(n); for (int i = 0; i <n ; ++i) { cin>>g[i]; gg[i]={g[i],i}; } sort(gg.begin(),gg.end()); int sum=m,x=0; for (int i = 0; i <n ; ++i) { if(sum>=gg[i].first){ sum-=gg[i].first; x++; } else break; } if(x==0) { cout << n + 1 << endl; return; } int flag=0; for (int i = 0; i <x ; ++i) { if(gg[i].second==x){ flag=1; break; } } if(flag){ cout<<max(1ll,n-x)<<endl; return; } // cout<<sum<<endl; if(sum+gg[x-1].first>=g[x]){ cout<<max(1ll,n-x)<<endl; return; } cout<<n-x+1<<endl; } signed main() { int t = 1; cin >> t; for (int i = 1; i <=t ; ++i) { k++; solve(); }
}