杂题选讲 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 img评测说明 : 1S
问题描述

img

输入格式

一行三个整数b,d,n。

输出格式

一行一个数表示模7528443412579576937 之后的结果。

样例输入 1

1 5 9

样例输出 1

76

样例输入 2

11 125 6715504

样例输出 2

1499928102740042526

提示

img


题解:

本题面向数据编程=_=;

首先,我们可以发现,原式可以转换为求:

\((\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)));
}

posted @ 2018-10-29 00:34  Katoumegumi  阅读(75)  评论(0编辑  收藏  举报
返回顶部