[20181130]校內模擬賽
經歷了NOIp2018的11月就這麼結束了呢
感覺還是很弱很弱的樣子,和一年前所想象的今天的自己差距甚遠
不得不懷疑自己這一年究竟做了什麼······
也許是什麼都沒做吧
但是說自己弱又有什麼用呢
zy:這樣的言行只不過是一種發洩罷了
(當然,這是對於像我這種菜雞而言的)
嗯,就這樣吧
這是一場,我一題都沒有A的比賽。
所以我覺得不寫點什麼對不起自己滴良心······
ABC107-D-Median of Medians
傳送門
Solution
題意是求一個序列中"所有子序列的中位數"所組成的序列的中位數
首先二分一個答案,這樣,所有小於它的都可以看成是-1,大於等於它的是1
如果一個區間的和\(\geq 0\),那麼它的中位數就不小於二分的答案
求區間數的話,對字首和求逆序對就行了
#include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define swap(x,y) (x^=y^=x^=y) inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } #define MN 100005 int n,m,a[MN],b[MN],c[MN]; int t[MN<<1]; void C(int x){for(;x<MN*2;x+=(x&-x))t[x]++;} int G(int x){int res=0;for(;x;x-=(x&-x))res+=t[x];return res;} inline bool check(int x) { ll cnt=0ll; register int i; for(i=1;i<=n;++i) c[i]=c[i-1]+(a[i]>=x?1:-1); memset(t,0,sizeof t); for(i=1;i<=n;++i) { C(c[i-1]+MN); cnt+=G(c[i]+MN); } return cnt*4>=1ll*n*(n+1); } int main() { n=read();register int i; for(i=1;i<=n;++i) a[i]=b[i]=read(); std::sort(b+1,b+n+1); m=std::unique(b+1,b+n+1)-b-1; int l,r,ans; for(l=1,r=m,ans=0;l<=r;) { int mid=l+r>>1; if(check(b[mid])) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",b[ans]); }
ARC101-E-Ribbons on Tree
傳送門
Solution
題意:給定一棵點數為偶數的樹,要求有多少種將點兩兩配對的方案使得每一條邊至少被一對匹配點之間的最短路徑覆蓋。
容斥
令\(F(E)\)表示\(E\)中的邊不被覆蓋的方案數
\[ans=\sum_{E}^{}(-1)^{|E|}F(E)\]
令\(g(n)\)表示大小為n的序列中有多少中配對方法,當n為偶數時
\[g(n)=\frac{n!}{(\frac{n}{2})!\cdot 2^{\frac{n}{2}}}=(n-1)\cdot (n-3)\cdot ......\cdot 3\cdot 1 \]- 當E確定時,原樹被分成若干個聯通塊,且我們的點對必須同處一個塊內
- 令
f[i][j]
表示以i為根的子樹內,有j個點是與i的父親節點同處一個聯通塊的方案數
然後就是樹形dp了,在列舉子樹的時候,雖然有三層的\(for\),但是可以發現是和子樹大小相關個,我們可以當作每次是選了兩個子樹做匹配,節點之間兩兩隻會配對一次,所以複雜度仍然是\(O(n^2)\)的- 根據容斥,在計算\(f[i][0]\)時,我們得變號
答案是\(-f[1][0]\),因為我們最後多變了一次號
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y)
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 5005
#define mod 1000000007
ll dp[MN][MN],g[MN],t[MN],siz[MN];
int hr[MN],en;
struct edge{int to,nex;}e[MN<<1];
inline void ins(int f,int t)
{
e[++en]=(edge){t,hr[f]};hr[f]=en;
e[++en]=(edge){f,hr[t]};hr[t]=en;
}
void dfs(int x,int fa){
siz[x]=1;dp[x][1]=1;register int i,j,k;
for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa)
{
dfs(e[i].to,x);
for(j=0;j<=siz[x];++j)
t[j]=dp[x][j],dp[x][j]=0;
for(j=0;j<=siz[x];++j)
for(k=0;k<=siz[e[i].to];++k)
(dp[x][j+k]+=t[j]*dp[e[i].to][k]%mod)%=mod;
siz[x]+=siz[e[i].to];
}
for(i=0;i<=siz[x];i+=2)
dp[x][0]=(dp[x][0]-dp[x][i]*g[i]%mod+mod)%mod;
}
int main(){
register int i,n,x;
n=read();
for(i=1;i<n;++i) x=read(),ins(x,read());
g[0]=1;for(i=2;i<=n;g[i]=g[i-2]*(i-1)%mod,i+=2);
dfs(1,0);
printf("%lld\n",mod-dp[1][0]);
return 0;
}
ARC101-F-Robots and Exits
傳送門
Solution
題意是有n個機器人和m個出口,每次可以將所有機器人的座標左移一格或者右移一格,當一個機器人正好在出口上時,它就會消失,問所有機器人消失的出口序列有多少種情況。
首先,機器人只會從左邊或右邊的第一個出口消失(除去那些最左邊最右邊和已經在出口上的)
- 一個點離左邊出口距離為a,右邊為i,我們可以把它看成是一個點
(a,b)。
那麼每次操作,就是把所有的點變成
(a-1,b)
或者(a,b-1)
。你可能會問,不應該是
(a-1,b+1)
或者(a+1,b-1)
嗎?其實上,當我們執行了一次操作後,那些正好碰到座標軸的點就已經消失,我們不要管它
而對於剩下的點,顯然它的座標在這個範圍內變化都是不會掉下去的,所以我們不妨以它的最靠近座標軸的座標作為它的座標
或者說,這個座標僅僅只是表示一個最大變化量而已
換一種理解方式,我們假設是原點從\((0,0)\)處開始向右或向左走,最後將圖分成兩個部分
上半部分的點掉進了左邊的出口(可以想象是y軸先碰到了它,也就是橫座標先變成了0),下半部分的點掉進了右邊的出口
我們要詢問有多少種這樣的劃線方式(兩條折線不同當且僅當它們分出的兩個部分是不同的)
設
f[i]
表示最後一個經過的點是i的方案數,最後一個的意思是,經過i之後,折線不再往上走也就是i是位於折線下方的最高的點
\[f[i]=1+\sum_{x_j<x_i,y_j<y_i}f[j]\]
我們用樹狀陣列就可以輕易維護
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y)
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 100005
#define mod 1000000007
int ans,x[MN],y[MN];
int cnt,a[MN],b[MN],B[MN],id[MN];
inline bool cmp(const int&o,const int&oo){return a[o]==a[oo]?b[o]<b[oo]:a[o]<a[oo];}
int t[MN],s[MN];
void C(int x,int v){for(;x<MN;x+=(x&-x))(t[x]+=v)%=mod;}
int G(int x){int r=0;for(;x;x-=(x&-x))(r+=t[x])%=mod;return r;}
int main()
{
register int i,j,n,m;
n=read(),m=read();
for(i=1;i<=n;++i) x[i]=read();
for(j=1;j<=m;++j) y[j]=read();
std::sort(x+1,x+n+1);std::sort(y+1,y+m+1);
for(i=1;i<=n;++i)
{
int p=std::lower_bound(y,y+m+1,x[i])-y;
if(p<=1||p>m||x[i]==y[p]) continue;
a[++cnt]=x[i]-y[p-1];B[cnt]=b[cnt]=y[p]-x[i];id[cnt]=cnt;
}
std::sort(B+1,B+cnt+1);
int Bcnt=std::unique(B+1,B+cnt+1)-B-1;
for(i=1;i<=cnt;i++) b[i]=std::lower_bound(B+1,B+Bcnt+1,b[i])-B;
std::sort(id+1,id+cnt+1,cmp);
for(i=1,j=1;i<=cnt;++i)
{
if(a[id[i]]==a[id[i-1]]&&b[id[i]]==b[id[i-1]]) continue;
for(;a[id[j]]<a[id[i]];++j) if(a[id[j]]!=a[id[j-1]]||b[id[j]]!=b[id[j-1]]) C(b[id[j]],s[j]%mod);
s[i]=1+G(b[id[i]]-1);
(ans+=(s[i]%mod))%=mod;
}
printf("%d\n",ans+1);
return 0;
}
Blog來自PaperCloud,未經允許,請勿轉載,TKS!