2018 Multi-University Training Contest 2 部分簡單題解析
Preface
多校第二場,依靠罰時優勢打到了校內的Rank 2
暴力分塊碾標算系列
T4 Game
題目大意:在一個數集\([1,n]\)中兩個人輪流選擇其中的一個數,並從數集中刪去這個數所有約數。先將所有數刪去的人獲勝。
比賽的時候手玩了\(n<=5\)的情況發現TM的怎麽總是先手贏啊,然後就認為這是個先手必勝的遊戲然而真的是這樣
賽後聽法老講了真正的做法,分假定情況討論:
- 假設這個狀態先手必勝,那就讓先手勝。
- 假設這個狀態後手必勝,那麽先手把\(1\)取走之後相當與把這個必敗態扔給了後手(因為\(1\)對後面的選取沒有任何影響)
故先手必勝,CODE
#include<cstdio> using namespace std; int n; int main() { while (scanf("%d",&n)!=EOF) puts("Yes"); return 0; }
T10 Swaps and Inversions
題目大意:給出一個數列,你可以花費\(x\)的代價交換兩個數。在交換結束之後,還要花費\(y*逆序對個數\)的代價。問最小的代價。
這個一個結論:交換次數等於逆序對個數,因此樹狀數組求逆序對之後乘上\(\min(x,y)\)即可。
CODE
#include<cstdio> #include<cctype> #include<algorithm> #include<cstring> using namespace std; const int N=100005; int a[N],bit[N],b[N],n,m,x,y; long long ans; inline int find(int x) { int l=1,r=m,mid; while (l<=r) { mid=l+r>>1; if (b[mid]==x) return mid; if (b[mid]<x) l=mid+1; else r=mid-1; } } inline int lowbit(int x) { return x&-x; } inline int get(int x) { int res=0; for (;x<=m;x+=lowbit(x)) res+=bit[x]; return res; } inline void add(int x) { for (;x;x-=lowbit(x)) ++bit[x]; } int main() { register int i; while (scanf("%d%d%d",&n,&x,&y)!=EOF) { for (i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i]; memset(bit,0,sizeof(bit)); sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1; for (ans=0,i=1;i<=n;++i) { a[i]=find(a[i]); ans+=get(a[i]+1); add(a[i]); } printf("%lld\n",ans*(x<y?x:y)); } return 0; }
T7 Naive Operations
題目大意:這題不用翻譯也能看懂吧,畢竟都是常見套路。給你兩個數組\(a,b\),初始時\(a\)的所有元素都為\(0\)。然後定義兩種操作:
- \(add\ l\ r\):將\(a_i,i\in[l,r]\)都加一
- \(query\ l\ r\):詢問\(\sum_{i=l}^r \lfloor \frac{a_i}{b_i}\rfloor\)
官方給出的解法是搞兩棵線段樹,然後一波操作反正我不會
比賽的時候YY了一個延時修改的分塊,2000+ms卡過。
首先我們記錄一個塊內的答案,然後考慮什麽時候這個值才會被修改。
當然是有個數\(i\)(或多個)的值被累加到多出一個\(b_i\)
有了這個思想,我們再維護一下每一塊內最少還要整塊累加多少次就會使答案發生改變,記作\(v_i\)。
然後修改的時候兩端還是暴力改,整塊的話也弄一個標記。
然後核心的來了,我們修改的時候不更新整塊標記,而是查詢的時候改
查詢還是先暴力計算兩端,然後對於被查詢的塊,看一下整塊的累加是否已經超過\(v_i\),是的話再更新。
一般情況下速度良好,大致\(O(n\sqrt n\cdot k)\),\(k\)為常數,大致在\([In^2\ n,In\ n]\)吧,其實主要還是和\(b_i\)的關系比較大。
塊樂的CODE
#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=100005,BLO=320;
int n,m,a[N],b[N],l,r,blk[N],v[BLO],t[BLO],size;
LL sum[BLO]; string s;
inline int min(int a,int b)
{
return a<b?a:b;
}
inline void reset(int id)
{
register int i; v[id]=1e9;
for (i=(id-1)*size+1;i<=id*size;++i)
{
sum[id]-=a[i]/b[i]; a[i]+=t[id];
sum[id]+=a[i]/b[i]; v[id]=min(v[id],b[i]-a[i]%b[i]);
} t[id]=0;
}
inline void modify(int l,int r)
{
register int i;
for (i=l;i<=min(blk[l]*size,r);++i)
{
if (!(++a[i]%b[i])) ++sum[blk[l]]; v[blk[l]]=min(v[blk[l]],b[i]-a[i]%b[i]);
}
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i)
{
if (!(++a[i]%b[i])) ++sum[blk[r]]; v[blk[r]]=min(v[blk[r]],b[i]-a[i]%b[i]);
}
for (i=blk[l]+1;i<=blk[r]-1;++i) ++t[i];
}
inline LL query(int l,int r)
{
register int i; LL tot=0;
for (i=l;i<=min(blk[l]*size,r);++i)
tot+=(a[i]+t[blk[l]])/b[i];
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) tot+=(a[i]+t[blk[r]])/b[i];
for (i=blk[l]+1;i<=blk[r]-1;++i)
{
if (t[i]>=v[i]) reset(i); tot+=sum[i];
}
return tot;
}
int main()
{
//freopen("7.in","r",stdin); freopen("7.out","w",stdout);
ios::sync_with_stdio(false); register int i;
while (cin>>n>>m)
{
memset(a,0,sizeof(a)); memset(v,63,sizeof(v));
memset(t,0,sizeof(t)); memset(sum,0,sizeof(sum));
for (size=sqrt(n),i=1;i<=n;++i)
cin>>b[i],blk[i]=(i-1)/size+1,v[blk[i]]=min(v[blk[i]],b[i]);
while (m--)
{
cin>>s; cin>>l>>r;
if (s[0]==‘a‘) modify(l,r); else cout<<query(l,r)<<endl;
}
}
return 0;
}
Postscript
主要是其他隊伍要麽沒寫出T7要麽想線段樹花了一段時間。
不過還是很高興的,出題人用心出題目,用腳造數據
2018 Multi-University Training Contest 2 部分簡單題解析