Codeforces Round #515 (Div. 3) ABCDEF比賽總結
做了4題,ABCE。感覺不是很滿意。1、D題題意讀不懂,剛開始想法對了,但是可惜題意讀錯了一直WA。2、F題沒來得及看。
感覺本場比賽的題目還算可以。
A:三行系列。讀懂題意就能做。因為只除去了中間的部分。但是感覺放在div3的A題還是稍微難了點。
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn=200010; int n,m,k,v; int a[maxn],sum[maxn]; int c[maxn]; int ans,ct,cnt,tmp,flag; char s[maxn]; int main() { int T,cas=1; scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&n,&m,&k,&v); ans=n/m; tmp=v/m-(k-1)/m; printf("%d\n",ans-tmp); } return 0; }
B:感覺這題有點坑。給你一01序列,長度為n,再給你r,1的地方可以種灑水器,範圍是(pos-r+1,pos+r-1)。pos是為1的位置。
問你至少種多少灑水器可以覆蓋1~n。如果不能輸出-1。直接模擬即可。不過細節挺多,樣例給的還算好。注意-1。
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn=200010; int n,m,k,v; int a[maxn],sum[maxn]; int c[maxn]; int ans,ct,cnt,tmp,flag; char s[maxn]; int main() { int T,cas=1; scanf("%d%d",&n,&m); ans=-1; for(int i=1;i<=n;i++) {scanf("%d",&a[i]);if(a[i])ans=0;} int r=0; int i=1; while(i<=n) { int tmp=ans,fg=0; for(;i<=n;i++) if(a[i]&&i-m+1<=r+1){fg=i;} else if(a[i]&&i-m+1>r+1) break; if(fg) ans++; r=fg+m-1; i=fg+1; if(r>=n) break; if(!fg) {ans=-1;break;} } printf("%d\n",ans); return 0; }
C:有一個書架(只有一層),m次操作,每次操作為 一個字元+x 如果字元為'L'或'R',則放上編號為x的書。放在最左邊或最右邊。如果是'?' x 則查詢至少移除多少本書才能使編號為x的書位於最左邊或最右邊。
不知道別人思路怎麼樣,我的第一感覺就是樹狀陣列。從中間分開,200005和200006開始分別往左右放,詢問的時候查詢啷個值比較一下就行了。
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn=400010; int n,m,k,v; int a[maxn],mp[maxn]; int c[maxn]; int ans,ct,cnt,tmp,flag; char s[maxn]; int lb(int x){return x&(-x);} void add(int i,int v) { while(i<maxn) { c[i]+=v; i+=lb(i); } } int query(int i) { int ans=0; while(i>0) { ans+=c[i]; i-=lb(i); } return ans; } int main() { int T,cas=1; scanf("%d",&n); memset(c,0,sizeof(c)); int l=200005,r=200006; for(int i=0;i<n;i++) { scanf("%s %d",s,&m); if(s[0]=='L') {mp[m]=l--;add(l+1,1);} else if(s[0]=='R') {mp[m]=r++;add(r-1,1);} else { ans=min(query(mp[m]-1),query(400008)-query(mp[m])); printf("%d\n",ans); } } // if(flag) puts("Yes"); else puts("No"); return 0; }
D:這題題意很迷啊。我感覺題意說的有問題。實際上就是給你n個玩具,m個箱子,每個箱子容量為k,每個玩具體積為a[i]。
從第一個玩具開始依次裝,如果能把所有玩具裝進去就停止。否則到了一個玩具,已經沒有足夠的箱子容量裝了的話,他就會把現在所有箱子裡最早裝的玩具扔掉。直到當前玩具能裝進去。問最後箱子裡一共能裝多少玩具。
思路:如果題意像我說的這麼簡單,那麼這道題就可以當做A題了(然而出題人非要整的花裡胡哨亂七八糟看不懂的)。顯然只需要從後往前貪心裝,裝不下就停。這時裝的玩具數量就是答案。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=400010;
ll n,m,k,v;
ll a[maxn],sum[maxn];
ll c[maxn],ed[maxn],be[maxn];
ll ans,ct,cnt,tmp,flag;
char s[maxn];
int main()
{
int T,cas=1;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=0;i<=max(2*n,2*m);i++)
c[i]=k;
int box=0,l=1,ans=0,tmp=0,st=0,aa=0;
for(int i=n;i>=1;i--)
{
if(c[box]>=a[i])
{
c[box]-=a[i];
be[i]=box;
tmp++;
ans=max(ans,tmp);
}
else if(box+1<m)
{
box++;
c[box]-=a[i];
be[i]=box;
tmp++;
ans=max(ans,tmp);
}
else break;
}
printf("%d\n",ans);
// if(flag) puts("Yes"); else puts("No");
return 0;
}
E:
給你兩個二進位制01串,長度分別為n,m(n,m<=2e5)。求第一個串&第二個串的十進位制和。每&一次,就去掉第二個串的最後一位繼續&,直到第二個串為空。
觀察可以發現,只需要維護一個2的n次方的字首和就行了。m>n時,第二個串的前m-n位一定是每一個1都會與第一個串每一個1&一次,後面的n位就依次往後了。m<n時,第二個串每個1就只有機會&第一個串中後n-m個1了。可以畫圖理解一下。(題意說清楚就比D題簡單了)
我用的快速冪,其實可以不用的,不用就可以O(n)解決。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=400010;
const ll mo=998244353;
ll n,m,k,v;
ll a[maxn],sum[maxn];
ll c[maxn],ed[maxn],be[maxn];
ll ans,ct,cnt,tmp,flag;
char s[maxn],t[maxn];
ll power(ll a,ll n) //a的n次方mod
{
ll ans=1;
a=a%mo;
while (n)
{
if(n&1) ans=(ans*a)%mo;
n>>=1;
a=(a*a)%mo;
}
return ans;
}
int main()
{
int T,cas=1;
scanf("%lld%lld",&n,&m);
scanf("%s%s",s,t);
sum[n+1]=0;ans=0;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='0') sum[i]=sum[i+1];
else sum[i]=(sum[i+1]+power(2,n-i-1))%mo;
}
if(m>n)
{
for(int i=0;i<m-n;i++)
if(t[i]=='1'){
ans=(ans+sum[0])%mo;
}
for(int i=m-n,j=0;i<m;i++,j++)
if(t[i]=='1'){
ans=(ans+sum[j])%mo;
}
}
else
{
for(int i=0,j=n-m;i<m;i++,j++)
if(t[i]=='1'){
ans=(ans+sum[j])%mo;
}
}
printf("%lld\n",ans);
// if(flag) puts("Yes"); else puts("No");
return 0;
}
F:(這道題在區域賽上可能算個簽到題吧。。。)
給你二維平面的n個點(n<=2e5) 以每個點的max(x,y)分層,你從(0,0)點開始走,每一步只能走上下左右長度為1,你要先走到第一層,走過第一層的所有點後,才能走第二層,以此類推,求走完最後一層的所有點,最少需要多少步。
由於要分層,就用一個vector存一下每一層的點。觀察發現每一層最左上的點到最右下的點,一定有一條路徑可以經過這一層的所有點。顯然這就是走遍這一層最短步數路線。於是每一層的點,按x從小到大排序,x相同按y從大到小排序。畫個圖一下就懂了。於是dp方程也有了。設dp[i][0]為走完第i層且位於這一層的最左上的點的最少步數。則v[i][]表示第i層的所有點(排序後的)
t1和t2為離散化後第i-1和第i層的點的編號。於是有
dp[i][0]=min(dp[i-1][1]+dist(v[t1].back(),v[t2].back()),dp[i-1][0]+dist(v[t1][0],v[t2].back()))+dist(v[t2][0],v[t2].back());
同樣,設dp[i][1]為走完第i層且位於這一層的最右下的點的最少步數。則有
dp[i][1]=min(dp[i-1][1]+dist(v[t1].back(),v[t2][0]), dp[i-1][0]+dist(v[t1][0],v[t2][0]))+dist(v[t2][0],v[t2].back());
最後min(dp[最後一層][0],dp[最後一層][1])就是答案。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=1e6+10;
map<int,int>mp;
int cnt,n;
vector<pair<ll,ll> >v[maxn];
ll dp[maxn][2];
ll dist(pair<ll,ll> a,pair<ll,ll> b)
{
return abs(a.first-b.first)+abs(a.second-b.second);
}
bool cmp(pair<ll,ll> a,pair<ll,ll> b)
{
return a.first<b.first||(a.first==b.first&&a.second>b.second);
}
int main()
{
scanf("%d",&n);
vector<ll>vv;
cnt=0;
for(int i=1;i<=n;i++)
{
ll l,r;
scanf("%lld%lld",&l,&r);
ll c=max(l,r);
if(mp[c]==0)
{
mp[c]=++cnt;
vv.push_back(c);
}
v[mp[c]].push_back(make_pair(l,r));
}
vv.push_back(0);
mp[0]=++cnt;
v[cnt].push_back(make_pair(0,0));
sort(vv.begin(),vv.end());
for(int i=0;i<vv.size();i++)
dp[i][0]=dp[i][1]=inf;
dp[0][0]=dp[0][1]=0;
for(int i=1;i<vv.size();i++)
{
int t1=mp[vv[i-1]];
int t2=mp[vv[i]];
sort(v[t2].begin(),v[t2].end(),cmp);
dp[i][0]=min(dp[i-1][1]+dist(v[t1].back(),v[t2].back()),
dp[i-1][0]+dist(v[t1][0],v[t2].back()));
dp[i][0]+=dist(v[t2][0],v[t2].back());
dp[i][1]=min(dp[i-1][1]+dist(v[t1].back(),v[t2][0]),
dp[i-1][0]+dist(v[t1][0],v[t2][0]));
dp[i][1]+=dist(v[t2][0],v[t2].back());
}
printf("%lld\n",min(dp[vv.size()-1][0],dp[vv.size()-1][1]));
}