2018.10.17--多校聯測第二場測試總結
阿新 • • 發佈:2018-11-11
T1:貪心裸題,估計100分,實際得分100分,原題戳這
T2:Dp,估計50分,實際得分50分,暫時沒有在任何OJ上見過相似的題目。
T3:二維點計數,估計100分,實際得分100分,沒有見過類似題目
T1題解:把區間按r為第一關鍵字,l為第二關鍵字排序,轉折點從小到大排序對於兩個點x和y能滿足當前區間,那麼對於後面的區間只有三種情況:
1、x和y都可用
2、x和y都不可用
3、x不可用,y可用
所以選擇更小的x明顯更優,用stl去找x就可以了,程式碼如下:
#include <set> #include <cstdio> #include <algorithm> using namespace std; const int maxn=2e5+5; int n,m,ans; multiset<int>s; multiset<int>::iterator it; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } struct cows { int l,r; bool operator<(const cows &a) const { if(r==a.r)return l<a.l; return r<a.r; } }c[maxn]; int main() { freopen("dream.in","r",stdin); freopen("dream.out","w",stdout); m=read();n=read(); for(int i=1;i<=m;i++) c[i].l=read(),c[i].r=read(); sort(c+1,c+m+1); for(int i=1;i<=n;i++) { int tmp=read(); s.insert(tmp); } for(int i=1;i<=m;i++) { it=s.lower_bound(c[i].l); if(it!=s.end()&&*it<=c[i].r) ans++,s.erase(it); }printf("%d\n",ans); return 0; }
T2:預處理dp[i][j]表⽰i個點的森林,有j個點在第⼀棵樹的概率,轉移考慮第i個點是否在第⼀棵⼦樹中,我們有狀態轉移⽅程
\[dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i]\]
考慮修改演算法三中狀態的含義,令f[i][j]表⽰有i個點的樹,深度不超過j的概率,g[i][j]表⽰有i個點的森林,深度不超過j的概率,f[i][j]直接從g[i-1][j-1]轉移
來;g[i][j]考慮列舉第⼀棵樹的⼤⼩k,從⼀棵樹和⼀個森林轉移來,同時還要乘上第⼀棵⼦樹⼤⼩為k的概率,我們有狀態轉移⽅程:
\[g[i][j]=\sum\limits_{k=1}^if[k][j]*g[i-k][j]*dp[i][k]\]
具體的細節可以⻅標程。最後只要⽤f[n][j]-f[n][j-1]就可以得到深度為j的樹的概率。
程式碼如下:
#include <cstdio> #include <algorithm> using namespace std; int n,pps; int inv[205],f[205][205]; int dp[205][205],g[205][205]; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } void prepare() { inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=1ll*(pps-pps/i)*inv[pps%i]%pps; } int main() { n=read(),pps=read(); prepare(); dp[1][1]=1; for(int i=2;i<=n;i++) for(int j=1;j<=i;j++) { dp[i][j]=1ll*dp[i-1][j-1]*(j-1)%pps*inv[i]%pps; dp[i][j]=(dp[i][j]+1ll*dp[i-1][j]*(i-j)%pps*inv[i]%pps)%pps; } for(int i=0;i<=n;i++) g[0][i]=g[1][i]=f[0][i]=f[1][i]=1; g[1][0]=0; for(int i=2;i<=n;i++) for(int j=1;j<=n;j++) { f[i][j]=g[i-1][max(0,j-1)]; for(int k=1;k<=i;k++) g[i][j]=(g[i][j]+1ll*f[k][j]*g[i-k][j]%pps*dp[i][k]%pps)%pps; } int ans=pps-1; for(int i=2;i<=n;i++) { int tmp=((f[n][i]-f[n][i-1])%pps+pps)%pps; ans=(ans+1ll*tmp*i%pps)%pps; } ans=(ans%pps+pps)%pps; printf("%d\n",ans); return 0; }
T3:對於這道題有個非常簡單的性質然而我考場上居然想了2小時!!!!,那就是對於一個區間[l,r]的答案就是r-l+1-cnt,cnt就是兩端都在[l,r]內的邊的個數。知道這個性質就是個二維偏序裸題了………………
程式碼如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define low(i) ((i)&(-i))
const int maxn=2e5+5;
int n,q;
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
struct fake {
int opt,id;
int l,r,ans;
void init(int a,int _id) {
l=read(),r=read();
if(l>r)swap(l,r);
opt=a;id=_id;
}
bool operator<(const fake &a)const {
if(r==a.r)return opt<a.opt;
return r<a.r;
}
}p[maxn*2];
struct Tree_array {
int c[maxn];
void add(int pos) {
for(int i=pos;i<=n;i+=low(i))
c[i]++;
}
int query(int pos) {
int sum=0;
for(int i=pos;i;i-=low(i))
sum+=c[i];
return sum;
}
}T;
bool cmp(fake a,fake b) {
return a.id<b.id;
}
int main() {
freopen("icekingdom.in","r",stdin);
freopen("icekingdom.out","w",stdout);
n=read(),q=read();
for(int i=1;i<n;i++)
p[i].init(0,0);
for(int i=n;i<n+q;i++)
p[i].init(1,i);
sort(p+1,p+n+q);
for(int i=1;i<n+q;i++) {
if(p[i].opt) p[i].ans=p[i].r-p[i].l+1-(T.query(p[i].r)-T.query(p[i].l-1));
else T.add(p[i].l);
}
sort(p+1,p+n+q,cmp);
for(int i=n;i<n+q;i++)
printf("%d\n",p[i].ans);
return 0;
}