A.EASY0
本體考察選手是否會寫代碼,取模後分類討論即可。
#include<bits/stdc++.h>
using namespace std;
int main(){
int x;
cin>>x;
x%=4;
if(x==1)cout<<"0 A"<<endl;
if(x==3)cout<<"2 A"<<endl;
if(x==2)cout<<"1 B"<<endl;
if(x==0)cout<<"1 A"<<endl;
}
B.EASY1
麻將題,分類討論,分成三張同類/兩張同類/各不同類三種情況進行考慮。
本體爲模擬題,考察選手是否有較爲縝密的思維和多階段的編程能力。建議先在草稿紙上寫下思路,分類情況,判斷方法,再實現代碼。
#include<bits/stdc++.h>
using namespace std;
int a[3];
int main(){
string s1,s2,s3;
cin>>s1>>s2>>s3;
if(s1[1]==s2[1]&&s1[1]==s3[1]){
a[0]=s1[0];
a[1]=s2[0];
a[2]=s3[0];
sort(a,a+3);
if(a[0]==a[2])cout<<0<<endl;
else if(a[0]==a[1]-1&&a[1]==a[2]-1)cout<<0<<endl;
else if(a[0]==a[1]||a[1]==a[2])cout<<1<<endl;
else if(a[0]==a[1]-1||a[1]==a[2]-1)cout<<1<<endl;
else if(a[0]==a[1]-2||a[1]==a[2]-2)cout<<1<<endl;
else cout<<2<<endl;
}else{
if(s1[1]!=s2[1]){
if(s1[1]==s3[1])swap(s2,s3);
else if(s2[1]==s3[1])swap(s1,s3);
}
if(s1[1]!=s2[1])cout<<2<<endl;
else{
a[0]=s1[0];
a[1]=s2[0];
sort(a,a+2);
if(a[0]==a[1]||a[0]==a[1]-1||a[0]==a[1]-2)cout<<1<<endl;
else cout<<2<<endl;
}
}
}
PS:打麻將是ACM中重要的知識點,希望大家有空多打麻將。
C.EASY2
仍然是模擬題,先對當前頁上的所有數字進行操作,然後翻頁,然後重複上述操作。但是有一個問題需要注意,如果每頁上的數字特別少,而頁碼特別多的話,可能會因爲重複的翻頁操作導致超時,所有用一個O(1)的技巧直接獲得新的頁碼,使用整數除法+取餘:
即:
LL rmn=lst%k;
st=(p[c]/k)*k+rmn;
if(lst<p[c])lst+=k;
另:開longlong。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL p[100005];
LL n,m,k;
LL cnt=0;
LL sv=0;
int main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(LL i=0;i<m;++i)scanf("%lld",&p[i]);
LL lst=k;
LL c=0;
while(c<m){
if(p[c]<=lst){
++c;
++sv;
}else if(sv){
lst+=sv;
sv=0;
++cnt;
}else{
LL rmn=lst%k;
lst=(p[c]/k)*k+rmn;
if(lst<p[c])lst+=k;
}
}
if(sv)++cnt;
cout<<cnt<<endl;
}
D.EASY3
和第一題同屬不需要動腦的簡單題,放在easy組最後一個是爲了充分論證“題目難度不按升序排列”。
直接找到最大值,然後每個元素進行比對即可。
我找不到我自己的代碼了,所以隨便找了一個寫的比較清楚的ac代碼。
#include<stdio.h>
int main(void)
{
int n,max=-1,tol=0;
scanf("%d",&n);
int a[n-1];
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<n;i++)
{
if(a[i]>max) max=a[i];
}
for(int i=0;i<n;i++)
{
tol+=max-a[i];
}
printf("%d",tol);
}
E。HARD0
題源hdu1356.
https://blog.csdn.net/qq_42778110/article/details/83958457
題解較長,見上鍊接。需要的基礎知識有:基礎數論(同餘,取模,gcd)
F.HARD1
前置知識:排序
題目意思是,給你一個n元數組(),每次操作,你可以任選一些元素,使其+1 再對m取模。問至少多少次操作可以使得這個數列單調不減。
先看數據範圍,1e5數量級,那麼上限是nlogn的算法。不難發現答案滿足單調性質,所以我們考慮二分答案,希望設計出O(n)內的check算法。這樣的複雜度至少要從左往右掃一遍每個元素,在掃描的過程中,如果左邊的元素更小,對於右邊的元素來說是嚴格更優的,這樣就滿足貪心的性質。所以我們考慮使用貪心法來解決問題。
現在我們假設x次操作可以實現目標,因爲有n個元素,那麼至少需要從左往右掃一遍。當我們考慮第k個元素,事實上滿足如下性質:
且:
那麼我們可以先利用第一個公式對每個元素用O(1)時間求出的可行域,然後再判斷在可行域中最小可以取什麼值,這同樣可以在O(1)時間內解決。所以最終複雜度就是O(nlogn),滿足要求。
在實際編碼是,先二分答案,每次二分從左往右掃一邊,保存一個pre值表示之前一個數的大小,然後對於每個數求出其可行域,然後在可行域中找到最小的>=pre的值。
代碼如下:
/*a[] 表示原數組*/
/*t[]表示如何保存可行域,$a_0,a_1;a_2,a_3$分別保存兩個閉區間的端點*/
#include<bits/stdc++.h>
using namespace std;
template <class T>
T read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
x*=f;return x;
}
int n,m;
int a[300005],pre,t[4];
int l,r;
bool check(int x){
pre=0;
for(int i=0;i<n;++i){
t[0]=t[1]=-1;
if(a[i]+x>=m){
t[0]=0;
t[1]=a[i]+x-m;
}
t[2]=a[i];
t[3]=min(a[i]+x,m-1);
if(t[0]<=pre&&pre<=t[1])pre=pre;
else if(t[1]+1<=pre&&pre<=t[2]-1)pre=t[2];
else if(t[2]<=pre&&pre<=t[3])pre=pre;
else return 0;
}
return 1;
}
int main(){
read(n),read(m);
for(int i=0;i<n;++i)read(a[i]);
l=-1,r=m;
while(r>l+1){
int m=(l+r)>>1;
if(check(m))r=m;
else l=m;
}
cout<<r<<endl;
return 0;
}
G.HARD2
前置知識:無(甚至不需要會博弈論)
如果先手選手第一步動完不立刻輸掉的話,最後所有的石子會變成0-1-2-…-(n-1),因爲不能有任何兩列相同。所以分兩種情況討論,如果先手第一步不輸,那麼根據初末狀態的奇偶性可以判斷;接下來只要考慮第一步直接失敗的情況。
我們對石子個數排序,有以下三種情況會暴斃:
1.存在兩對或以上相同的石子數(包括三連),這樣怎麼搞選完都有至少一對相同;
2.存在一對相同的且存在一堆石子比這堆石子剛好少一個,這樣選這對相同的就會出現一對新的相同的;
3.存在兩個或以上0,這樣你隨便怎麼搞,選完還是有至少兩個0。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int n;
LL a[100005];
int flag=0;
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i)scanf("%lld",&a[i]);
sort(a,a+n);
for(int i=0;i<n-1;++i){
if(a[i]==a[i+1]){
if(i&&(a[i]==a[i-1]||a[i]==a[i-1]+1)){
cout<<"cslnb"<<endl;
return 0;
}
if(a[i]==0){
cout<<"cslnb"<<endl;
return 0;
}
if(flag++){
cout<<"cslnb"<<endl;
return 0;
}
}
}
LL sum=0;
for(int i=0;i<n;++i)sum=(sum+a[i]-i)%2;
cout<<(sum?"sjfnb":"cslnb")<<endl;
}
H.HARD3
前置知識:線段樹
看到這個數據規模應該很容易想到nlogn數據結構按順序遞推logn求取狀態參量。
因爲向上開口,比較特殊,所以考慮從上到下遍歷。借用掃描線的思想,構造一顆單點覆蓋區間查詢的線段樹。
先對數據離散化,這樣只要建立一顆1-200000範圍的樹即可。
然後從上往下,對於一個縱座標y,我們考慮所有在這個y上出現的點的x值。我們把這些x值全部更新到線段樹上,總區間和tree[1]表示到當前y爲止,總共有多少個不同的x值出現。那麼很顯然的,,也就是所有可能的不同選法。但是要考慮一些其他情況,對於某個區間,如果在當前y上沒有新的x值產生(或者自己覆蓋自己),那麼這個區間其實和之前出現過的區間是一樣的,所有不應該被計入答案,所有用線段樹查詢相鄰兩個本y出現的x值之間歷史x值的個數,然後類似上述公式減去即可。
因爲每個點只被插入一次並且產生一次區間查詢,所有複雜度。
#include<bits/stdc++.h>
using namespace std;
struct node{
int x, y;
}a[200005];
int n;
long long ans=0;
int v[200005],top=0;
const int N=200005;
int tree[200006<<2];
//單點修改
void modify(int x,int k){
for(x+=N;x;x>>=1)tree[x]+=k;
}
//區間查詢
int query(int l,int r){
if(l>r)return 0;
int ans=0;
for(l=N+l-1,r=N+r+1;l^r^1;l>>=1,r>>=1){
if(~l&1)ans+=tree[l^1];
if(r&1)ans+=tree[r^1];
}
return ans;
}
bool cmp1(node a,node b){
return a.x<b.x;
}
bool cmp2(node a,node b){
if(a.y>b.y)return 1;
if(a.y<b.y)return 0;
return (a.x<b.x);
}
long long fun(int x){
long long r=x;
r=r*(r+1)/2;
return r;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp1);
int cnt=1;
int lst=a[1].x;
a[1].x=1;
for(int i=2;i<=n;++i){
if(a[i].x==lst)a[i].x=cnt;
else{
lst=a[i].x;
a[i].x=++cnt;
}
}
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;){
int ty=a[i].y;
top=0;
while(a[i].y==ty)v[top++]=a[i++].x;
for(int j=0;j<top;++j)modify(v[j],1-query(v[j],v[j]));
v[top++]=n+1;
ans+=fun(tree[1]);
for(int j=0;j<top-1;++j)ans-=fun(query(v[j]+1,v[j+1]-1));
ans-=fun(query(1,v[0]-1));
}
cout<<ans<<endl;
}
I.HARD4
前置知識:二分查找
這題是顯然的正面狀態巨多,答案具有單調性,且反面檢驗非常簡單的題。不用說,快樂二分。
我們考慮按照一開始的走法,我們會走出一個向量,同時題目會給你一個座標,那麼這中間就有一個偏移量,希望通過修改一些操作,使得這個可以被實現。
那麼我們二分答案,記錄當前二分到的區間長度爲len,那麼我們先把這個區間中的所有的操作都撤回,就有一個被撤回的向量,這個向量等於這個區間內操作的和向量,那麼就有新的。那麼接下來我們可以隨便填充這len個數,如果len>=(的曼哈頓距離)且他們奇偶性相同,那麼答案成立,反之答案不成立。由此可以二分得到結果。
#include<iostream>
#include<cstring>
using namespace std;
#define ABS(x) ((x)>=0?(x):-(x))
const int maxn=2e5+7;
int n,tarx,tary;
string s;
int u[maxn],d[maxn],l[maxn],r[maxn];
int delx,dely;
bool check(int len){
for(int ll=1;ll+len-1<=n;++ll){
int rr=ll+len-1;
int du=u[rr]-u[ll-1];
int dd=d[rr]-d[ll-1];
int dl=l[rr]-l[ll-1];
int dr=r[rr]-r[ll-1];
int dx=delx+dr-dl;
int dy=dely+du-dd;
int delta=ABS(dx)+ABS(dy);
if(delta<=len)return 1;
}
return 0;
}
int main()
{
cin>>n>>s>>tarx>>tary;
for(int i=1;i<=n;++i){
u[i]=u[i-1];
d[i]=d[i-1];
l[i]=l[i-1];
r[i]=r[i-1];
if(s[i-1]=='U')++u[i];
if(s[i-1]=='D')++d[i];
if(s[i-1]=='L')++l[i];
if(s[i-1]=='R')++r[i];
}
int nowx=r[n]-l[n],nowy=u[n]-d[n];
delx=tarx-nowx;
dely=tary-nowy;
int lo=-1,hi=n+1;
while(lo<hi-1){
int mid=(lo+hi)>>1;
if(check(mid))hi=mid;
else lo=mid;
}
if((delx+dely)&1)hi=n+1;
if(hi==n+1)cout<<-1<<endl;
else cout<<hi<<endl;
}