雜題選講 DAY1
T1 【FJOI2016】建築師
題面:
H【FJOI2016】建築師 |
---|
時間限制 : - MS 空間限制 : - KB 評測說明 : 1s 256m |
問題描述
小 Z 是一個很有名的建築師,有一天他接到了一個很奇怪的任務:在數軸上建 n 個建築,每個建築的高度是 1 到 n 之間的一個整數。
小 Z 有很嚴重的強迫癥,他不喜歡有兩個建築的高度相同。另外小 Z 覺得如果從最左邊(所有建築都在右邊)看能看到 A個建築,從最右邊(所有建築都在左邊)看能看到 B 個建築,這樣的建築群有著獨特的美感。現在,小 Z 想知道滿足上述所有條件的建築方案有多少種?
如果建築 i 的左(右)邊沒有任何建造比它高,則建築 i 可以從左(右)邊看到。兩種方案不同,當且僅當存在某個建築在兩種方案下的高度不同。
輸入格式
第一行一個整數 T,代表 T 組數據。
接下來 T 行,每行三個整數 n,A,B
輸出格式
對於每組數據輸出一行答案 mod10^9+7。
樣例輸入 1
2
3 2 2
3 1 2
樣例輸出 1
2
1
樣例輸入 2
5
1 1 1
2 1 1
4 3 1
10 2 2
8 6 4
樣例輸出 2
1
0
3
219168
0
提示
對於 10% 的數據 : 1≤n≤10
對於 20% 的數據 : 1≤n≤100
對於 40% 的數據 : 1≤n≤50000, 1≤T≤5
對於 100%的數據 :1≤n≤50000, 1≤A,B≤100, 1≤T≤200000
題解:
由於高度為\(N\)的建築物肯定不會被擋住,將這個建築作為分水嶺,將左右兩邊分開為\(A+B-1\)
顯然我們需要考慮將這\(N-1\)個人放在\(A+B-2\)個桌子上,這就是第一類斯特林數;
我們考慮可以將剩下的建築分為\(A-1\)與\(B-1\)兩部分,於是這樣的方案數可通過組合數求出;
\(code:\)
#include<cstdio> #include<iostream> #include<ctype.h> #include<cstring> #include<vector> #include<cmath> #include<map> #include<algorithm> #define reint register int #define ll long long #define rell register ll using namespace std; char buf[1<<20],*p1,*p2; inline char gc() { // return getchar(); return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++; } template<typename T> inline void read(T &x) { char tt; bool flag=0; while(!isdigit(tt=gc())&&tt!='-'); tt=='-'?(flag=1,x=0):(x=tt-'0'); while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0'); if(flag) x=-x; } const int mod=1e9+7; inline ll mul(ll a,ll b){return a*b<mod?a*b:a*b%mod;}; inline ll add(ll a,ll b){return a+b<mod?a+b:a+b-mod;}; ll c[202][202],s[50002][202]; void sent() { s[0][0]=1; for(rell i=1;i<=50001;i++) for(rell j=1;j<=200;j++) s[i][j]=add(s[i-1][j-1],mul(i-1,s[i-1][j])); c[0][0]=1; for(rell i=1;i<=201;i++) for(rell j=0;j<=i;j++) c[i][j]=j?add(c[i-1][j],c[i-1][j-1]):1; } ll t,n,a,b; int main() { sent(); read(t); while(t--) { read(n),read(a),read(b); printf("%lld\n",mul(s[n-1][a+b-2],c[a+b-2][a-1])); } }
T2 【CERC2017】旅遊指南
題面:
I【CERC2017】旅遊指南 |
---|
時間限制 : 20000 MS 空間限制 : 565536 KB SPJ 評測說明 : 1s,512m |
問題描述
給定一張n個點,m條雙向邊的無向圖。
你要從1號點走到n號點。當你位於x點時,你需要花1元錢,等概率隨機地買到與x相鄰的一個點的票,只有通過票才能走到其它點。
每當完成一次交易時,你可以選擇直接使用那張票,也可以選擇扔掉那張票然後再花1元錢隨機買另一張票。註意你可以無限次扔票。
請使用最佳的策略,使得期望花的錢數最少。
輸入格式
第一行包含兩個正整數n,m(1<=n,m<=300000),表示點數和邊數。
接下來m行,每行兩個正整數u,v(1<=u,v<=n),表示一條雙向邊。
輸入數據保證無重邊、無自環,且1號點一定可以走到n號點。
輸出格式
輸出一行一個實數,即最少的期望花費,當絕對或者相對誤差不超過10^{-6}時視為正確。
樣例輸入 1
5 8
1 2
1 3
1 4
2 3
2 4
3 5
5 4
2 5
樣例輸出 1
4.1111111111
樣例輸入 2
4 4
1 2
1 3
2 4
3 4
樣例輸出 2
3.0000000000
題解:
好題啊!妙啊!
我們設定狀態F[i]為從i點到終點所花錢數最少的期望,那麽顯然可以得到:
\(F[p]=\frac{\sum\ min(F[p],F[x_i])}{deg[p]}+1;\)
移項得:
\(deg[p]*F[p]=\sum min(F[p],F[x_i])+deg[p];\)
我們設在取最小值的過程中使用了\(a\)次\(F[x_i]\),那麽:
\(deg[p]*F[p]=(deg[p]-a)*F[p]+a*F[x_i]+deg[p];\)
\(F[p]*a=F[x]*a+deg[p];\)
\(F[p]=\frac{a*F[x]+deg[p]}{a};\)
初始狀態\(F[N]=0\),因為我們默然最開始所有的取最小處都是選擇的\(F[x]\),所以所得期望一定大於等於結果,通過小值去更新,即可使其接近或等於答案,這個貪心的過程我們註意到類似於迪傑斯特拉;
\(code:\)
#include<cstdio>
#include<iostream>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define reint register int
#define ll long long
#define ld double
#define rell register ll
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(flag=1,x=0):(x=tt-'0');
while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
if(flag) x=-x;
}
struct node{
int x;
ld len;
inline node(int a=0,ld b=0.0)
{x=a,len=b;}
inline bool operator<(node a)const
{return len>a.len;}
};
const int maxn=300002;
int n,m,deg[maxn];
int a[maxn];
vector<int>G[maxn];
priority_queue<node>q;
ld f[maxn],sum[maxn];
bool book[maxn];
void djs()
{
f[n]=0.0;q.push(node(n,0.0));
while(!q.empty())
{
int x=q.top().x;q.pop();
if(book[x]) continue;
book[x]=1;
// printf("%d--\n", G[x].size()-1);
for(reint i=G[x].size()-1;i>=0;i--)
{
int p=G[x][i];
if(book[p]) continue;
a[p]++;sum[p]+=f[x];
f[p]=(sum[p]+deg[p])/a[p];
q.push(node(p,f[p]));
}
}
}
int main()
{
read(n),read(m);
for(reint i=1;i<=m;i++)
{
int x,y;
read(x),read(y);
G[x].push_back(y);
G[y].push_back(x);
deg[x]++;deg[y]++;
}
djs();
printf("%.10lf",f[1]);
}
T3 【HAOI2017】供給側改革
題面:
J【HAOI2017】供給側改革 |
---|
時間限制 : - MS 空間限制 : - KB 評測說明 : 1s,256m |
問題描述
Anihc國提高社會生產力水平.落實好以人民為中心的發展思想。決定進行供給側結構性改革。
為了提高供給品質.你調查了某個產業近來n個時期的供求關系平衡情況.每個時期的情況都用0或1中的一個數字來表示.於是這就是—個長度為n的01字符串S。為了更好的了解這一些數據.你需要解決一些詢問.我們令data(l,r)表示:在字符串S中.起始位置在[l,r]之間的這些後綴之中,具有最長公共前綴的兩個後綴的最長公共前綴的長度。
對於每一個詢問L,R.求
ans=∑data(i,R) L≤i<R
由於你其實根本沒有時間調查,所以這些數據都是亂編的,即串S中的每一位都是在0和1之間隨機產生的。
輸入格式
第一行2個整數n,Q,表示字符串的長度,以及詢問個數
接下來一行長度為n的一個01串S
接下來Q行,每行2個整數L,R.一個詢問L.R
輸出格式
共Q行.每行一個整數.表示對應詢問的答案。
樣例輸入 1
6 3
010110
2 5
1 6
1 2
樣例輸出 1
4
6
0
樣例輸入 2
20 20
01010011000001000111
1 3
6 13
5 10
3 6
3 6
6 16
8 18
1 4
11 15
3 13
1 19
7 10
10 13
3 9
4 17
1 18
2 20
1 20
2 19
1 20
樣例輸出 2
3
22
20
4
4
33
26
5
7
34
59
12
6
10
42
54
56
60
55
60
提示
數據點 n的規模 Q的規模
1 <= 20 <= 20
2 <= 20 <= 20
3 <= 100 <= 100
4 <= 100 <= 100
5 <= 5000 <= 5000
6 <= 5000 <= 5000
7 <= 100000 <= 100000
8 <= 100000 <= 100000
9 <= 100000 <= 100000
10 <= 100000 <= 100000
對於所有的數據保證:n <= 100000,Q<= 100000,1<=L<R<=n,01串隨機生成。
題解:
未做呢,待補待補;
T4 平衡的隊列
題面:
輸入格式
第一行,兩個空格間隔的整數N和K
接下來N行,每行一個K位二進制整數(已轉換成了十進制),表示第i頭奶牛的特征
輸出格式
一行,一個整數,表示最大的平衡區間的長度
樣例輸入
7 3
7
6
7
2
1
4
2
樣例輸出
4
提示
樣例說明:這個範圍是3到6,其中每個特征恰好出現了2次。
題解:
記錄每一位上的前綴和,\(Hash\)一下,加上\(map\)看前面有沒有一樣的;
\(code:\)
#include<cstdio>
#include<iostream>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#include<algorithm>
#define reint register int
#define mod1 1000000007
#define mod2 998244353
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(flag=1,x=0):(x=tt-'0');
while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
if(flag) x=-x;
}
const int maxn=1e5+2;
int n,k;
ll hashh[maxn];
int sum[maxn][32];
map<ll,int>book;
int main()
{
read(n),read(k);
for(reint i=1;i<=n;i++)
{
ll x;
read(x);
for(int j=0;j<k;j++)
sum[i][j]=(x>>j&1)+sum[i-1][j];
}
// for(int i=1;i<=n;i++,putchar(10))
// for(int j=0;j<k;j++)
// printf("%d ",sum[i][j]);
for(reint i=1;i<=n;i++)
{
int tmp=sum[i][0];
for(reint j=0;j<k;j++)
sum[i][j]-=tmp;
}
for(reint i=0;i<=n;i++)
for(reint j=0;j<k;j++)
hashh[i]=hashh[i]*mod1+1ll*(1ll*sum[i][j]+mod2);
int ans=0;
for(reint i=0;i<=n;i++)
{
if(!book[hashh[i]]) book[hashh[i]]=i+1;
else ans=max(ans,i-book[hashh[i]]+1);
}
printf("%d",ans);
}
T5 [JLOI2015 DAY1]有意義的字符串
題面:
L [JLOI2015 DAY1]有意義的字符串 |
---|
時間限制 : - MS 空間限制 : 165536 KB 評測說明 : 1S |
問題描述
輸入格式
一行三個整數b,d,n。
輸出格式
一行一個數表示模7528443412579576937 之後的結果。
樣例輸入 1
1 5 9
樣例輸出 1
76
樣例輸入 2
11 125 6715504
樣例輸出 2
1499928102740042526
提示
題解:
本題面向數據編程=_=;
首先,我們可以發現,原式可以轉換為求:
\((\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n-(\frac{b-\sqrt{d}}{2})^n\)
這個式子,我們又觀察一下可以發現:
\(X_1=(\frac{b+\sqrt{d}}{2}),X_2=(\frac{b-\sqrt{d}}{2})\)
這是一個二次方程的兩個根:
\(X^2+b*X-\frac{b^2-d}{4}=0\)
然後呢,對我們要求的前半部分,是一個整數,後半部分值域為\((-1,0]\),這個由數據範圍可得;
我們設:
\(F(n)=X_1^n+X_2^n;\)
那麽易得:
\(X_1^n+X_2^n=(X_1+X_2)(X_1^{n-1}+X_2^{n-2})-X_1*X_2^{n-1}-X_2*X_1^{n-1}=(X_1+X_2)(X_1^{n-1}+X_2^{n-2})-(X_1*X_2)(X_1^{n-2}+X_2^{n-2})\)
由韋達定理:
\(X_1+X_2=b,X_1*X_2=\frac{d-b^2}{4};\)
那麽我們要求的就變成了:
\(F(n)=F(n-1)*b+F(n-2)*(\frac{d-b^2}{4});\)
這個顯然可以用矩陣快速冪優化;
考慮後半部分,我們只要看看它有沒有可能做出值為-1的貢獻即可(當且僅當\(n\)為奇數,且\(d!=b*b\))
\(code:\)
#include<cstdio>
#include<iostream>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define reint register int
#define ll long long
#define ld double
#define rell register ll
#define mod 7528443412579576937ul
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(flag=1,x=0):(x=tt-'0');
while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
if(flag) x=-x;
}
inline ll add(ll x,ll y){return x+y>=mod||x+y<0?x+y-mod:x+y;}
inline ll sub(ll x,ll y){return x-y<0?x-y+mod:x-y;}
inline ll mul(ll x,ll y){ll d=(ll)(x*(long double)y/mod+0.5);return sub(x*y,d*mod);}
struct arr{
ll n,m;
ll a[21][21];
ll *operator[](int b){return a[b];}
inline arr(ll c=0,ll d=0)
{
memset(a,0,sizeof(a));
n=c,m=d;
}
arr operator*(arr b) const
{
arr c;
c.n=n;c.m=b.m;
for(reint i=0;i<n;i++)
for(reint j=0;j<c.m;j++)
for(reint k=0;k<m;k++)
c[i][j]=add(c[i][j],mul(a[i][k],b[k][j]));
return c;
}
arr operator*=(arr a){
*this=*this*a;
return *this;
}
arr operator^(ll b)
{
arr ans;
ans=arr(n,m);
for(reint i=0;i<n;i++)
ans[i][i]=1;
while(b)
{
if(b&1ul) ans*=*this;
*this*=*this;
b>>=1ul;
}
return ans;
}
};
ll b,d,n;
arr aa,bb;
int main()
{
read(b),read(d),read(n);
if(!n) puts("1"),exit(0);
aa=arr(1,2);bb=arr(2,2);
aa[0][0]=b,aa[0][1]=2;
bb[0][0]=b,bb[0][1]=1;
bb[1][0]=(d-b*b)/4,bb[1][1]=0;
aa*=bb^(n-1);
printf("%lld",sub(aa[0][0],!(n&1ul)&&(d!=b*b)));
}
雜題選講 DAY1