zoukankan      html  css  js  c++  java
  • BJOI2018简要题解

    BJOI2018简要题解

    D1T1 二进制

    题意

    pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 (3) 的倍数。他想研究对于二进制,是否也有类似的性质。
    于是他生成了一个长为 (n) 的二进制串,希望你对于这个二进制串的一个子区间,能求出其有多少位置不同的连续子串,满足在重新排列后(可包含前导 (0))是一个 (3) 的倍数。两个位置不同的子区间指开始位置不同或结束位置不同。
    由于他想尝试尽量多的情况,他有时会修改串中的一个位置,并且会进行多次询问。

    对于 (20\%) 的数据,(1 leq n,m leq 100)

    对于 (50\%) 的数据,(1 leq n,m leq 5000)

    对于 (100\%) 的数据,(1 leq n,m leq 100000)(l leq r)

    题解

    打表后考虑不合法的区间:

    • 有奇数个1且0的个数少于2
    • 只有1个1

    由于太久前写的了还写了好久,用set乱搞就好了(我也忘记怎么搞的了,写了好长调了好久现在看不懂了QAQ)

    代码

    #include<iostream>
    #include<algorithm>
    #include<set>
    #define ll long long
    using namespace std;
    const int N=1e5+10;
    int n,m,a[N];
    ll val[N],cal2[N],t[N],tot[N],t2[N],val2[N];
    set<int> s,s1;
    int read() {int x;scanf("%d",&x);return x;}
    ll calc(int L,int R) {return 1ll*((L+1)/2)*((R+2)/2)+1ll*((L+2)/2)*((R+1)/2);}
    ll calc2(int L,int R)
    {
    	ll res=1ll*(L+1)*(R+1)-1;
    	if(L) res--;if(R) res--;
    	return max(0ll,res);
    }
    void add1(int x,ll k) {while(x<=n) t[x]+=k,x+=x&(-x);}
    ll query1(int x) {ll s=0;while(x) s+=t[x],x-=x&(-x);return s;}
    void add2(int x,ll k) {while(x<=n) t2[x]+=k,x+=x&(-x);}
    ll query2(int x) {ll s=0;while(x) s+=t2[x],x-=x&(-x);return s;}
    void WorkQ()
    {
    	int l,r,len;ll ans=0;
    	scanf("%d%d",&l,&r);len=r-l+1;
    	int pl=*s.lower_bound(l);
    	int pr=*(--s.upper_bound(r));
    	if(pl>r) return (void)printf("%lld
    ",tot[len]-cal2[r-l+1]);
    	if(pl==pr) return (void)printf("%lld
    ",tot[len]-calc(pl-l,r-pl)-cal2[pl-l]-cal2[r-pr]);
    	ans=query1(pr-1)-query1(pl);
    	int d1=*s.upper_bound(pl)-pl-1;ans+=calc(pl-l,d1)+cal2[pl-l];
    	int d2=pr-*(--s.lower_bound(pr))-1;ans+=calc(d2,r-pr)+cal2[r-pr];
    	pl=*s1.lower_bound(l);
    	pr=*(--s1.upper_bound(r));
    	if(pl==pr) ans+=calc2(pl-l,r-pr);
    	if(pl<pr)
    	{
    		d1=*s1.upper_bound(pl)-pl-1;ans+=calc2(pl-l,d1);
    		d2=pr-*(--s1.lower_bound(pr))-1;ans+=calc2(d2,r-pr);
    		ans+=query2(pr-1)-query2(pl);
    	}
    	printf("%lld
    ",tot[len]-ans);
    }
    void WorkM()
    {
    	int p=read();a[p]^=1;
    	if(a[p]==0)//1->0
    	{
    		s.insert(p);s1.erase(p);
    		set<int>::iterator fr,nt,ffr,nnt;
    		fr=s.lower_bound(p);fr--;
    		nt=s.upper_bound(p);
    		add1(p,-val[p]);
    		add1(p,val[p]=calc(p-*fr-1,*nt-p-1));
    		if(*fr!=0)
    		{
    			ffr=fr;ffr--;
    			add1(*fr,-val[*fr]);
    			add1(*fr,val[*fr]=calc(*fr-*ffr-1,p-*fr-1));
    		}
    		if(*nt!=n+1)
    		{
    			nnt=nt;nnt++;
    			add1(*nt,-val[*nt]);
    			add1(*nt,val[*nt]=calc(*nt-p-1,*nnt-*nt-1));
    		}
    		if(*nt-p-1>0)
    		{
    			add1(*nt-1,-val[*nt-1]);
    			add1(*nt-1,val[*nt-1]=cal2[*nt-p-1]);
    		}
    		if(p-*fr-1>0)
    		{
    			add1(p-1,-val[p-1]);
    			add1(p-1,val[p-1]=cal2[p-*fr-1]);
    		}
    		fr=s1.lower_bound(p);fr--;
    		nt=s1.upper_bound(p);
    		add2(p,-val2[p]),val2[p]=0;
    		if(*fr!=0)
    		{
    			ffr=fr;ffr--;
    			add2(*fr,-val2[*fr]);
    			add2(*fr,val2[*fr]=calc2(*fr-*ffr-1,*nt-*fr-1));
    		}
    		if(*nt!=n+1)
    		{
    			nnt=nt;nnt++;
    			add2(*nt,-val2[*nt]);
    			add2(*nt,val2[*nt]=calc2(*nt-*fr-1,*nnt-*nt-1));
    		}
    	}
    	else//0->1
    	{
    		s.erase(p);s1.insert(p);
    		set<int>::iterator fr,nt,ffr,nnt;
    		fr=s.upper_bound(p);nt=fr;fr--;
    		if(*fr!=0)
    		{			
    			ffr=fr;ffr--;
    			add1(*fr,-val[*fr]);
    			add1(*fr,val[*fr]=calc(*fr-*ffr-1,*nt-*fr-1));
    		}
    		if(*nt!=n+1)
    		{
    			nnt=nt;nnt++;
    			add1(*nt,-val[*nt]);
    			add1(*nt,val[*nt]=calc(*nt-*fr-1,*nnt-*nt-1));
    		}		
    		if(*nt-p-1>0) add1(*nt-1,-val[*nt-1]),val[*nt-1]=0;
    		if(p-*fr-1>0) add1(p-1,-val[p-1]),val[p-1]=0;
    		add1(p,-val[p]),val[p]=0;
    		add1(*nt-1,val[*nt-1]=cal2[*nt-*fr-1]);
    		fr=s1.lower_bound(p);fr--;
    		nt=s1.upper_bound(p);
    		add2(p,-val2[p]);
    		add2(p,val2[p]=calc2(p-*fr-1,*nt-p-1));
    		if(*fr!=0)
    		{
    			ffr=fr;ffr--;
    			add2(*fr,-val2[*fr]);
    			add2(*fr,val2[*fr]=calc2(*fr-*ffr-1,p-*fr-1));
    		}
    		if(*nt!=n+1)
    		{
    			nnt=nt;nnt++;
    			add2(*nt,-val2[*nt]);
    			add2(*nt,val2[*nt]=calc2(*nt-p-1,*nnt-*nt-1));
    		}
    	}
    }
    int main()
    {
    	cin>>n;
    	s.insert(0);s.insert(n+1);s1.insert(0);s1.insert(n+1);
    	for(int i=1;i<=n;i++) a[i]=read(),a[i]?s1.insert(i):s.insert(i);
    	for(int i=1;i<=n;i++) cal2[i]=(i&1)?(i+1)/2:cal2[i-1];
    	for(int i=1;i<=n;i++) cal2[i]+=cal2[i-1];
    	for(int i=1;i<=n;i++) tot[i]=tot[i-1]+i;
    	set<int>::iterator it=s.begin(),fr,nt;
    	for(;it!=s.end();it++)
    	{
    		if(*it==n+1)
    		{
    			fr=it,fr--;
    			if(*it-*fr-1>0) add1(*it-1,val[*it-1]=cal2[*it-*fr-1]);
    		}
    		if(*it==0||*it==n+1) continue;
    		fr=nt=it;fr--,nt++;
    		add1(*it,val[*it]=calc(*it-*fr-1,*nt-*it-1));
    		if(*it-*fr-1>0) add1(*it-1,val[*it-1]=cal2[*it-*fr-1]);
    	}
    	it=s1.begin();
    	for(;it!=s1.end();it++)
    	{
    		if(*it==0||*it==n+1) continue;
    		fr=nt=it;fr--,nt++;
    		add2(*it,val2[*it]=calc2(*it-*fr-1,*nt-*it-1));
    	}
    	for(cin>>m;m;m--) read()==1?WorkM():WorkQ();
    }
    

    D1T2 染色

    题意

    pupil 喜欢给图的顶点染颜色。有一天,master 想刁难他,于是给了他一个无重边和自环的无向图,
    并且对每个点分别给了一个大小为 (2) 的颜色集合,pupil 只能从这个集合中选一种颜色给这个点染色。master 希望 pupil 的染色方案使得没有两个有边相连的点被染了相同的颜色。

    现在 pupil 想知道,是否无论 master 的颜色集合是什么,他均有办法按照要求染色。

    对于 (10\%) 的数据,(1 leq n leq 3)

    对于 (20\%) 的数据,(1 leq n leq 6)

    对于 (50\%) 的数据,(1 leq n leq 1000)(0 leq m leq 2000)

    对于 (100\%) 的数据,(1 leq n leq 10000)(0 leq m leq 20000)(1 leq T leq 10)

    题解

    毫无思路。

    分以下几种情况讨论:

    • 存在奇环:直接所有点{A,B},NO。

    • 存在两个分离的环:NO。

      对于一个大小为4的环,构造{A,C},{B,C},{A,B},那么剩下那个{A,X}就一定只能选X。

      两个分离的环就这样构造,使得在连接两个环的路径上产生冲突即可。

    判完上述情况后,对于每个联通块这样考虑:

    • m<=n:YES。

    • m>=n+2:NO,一定存在两个分离的环。

    • m=n+1:依次删掉所有的叶子,最后剩下的一定是两个点之间有三条路径。

      可以证明,这三条路径有两条是2的时候YES,否则NO。

      对于三条路径都是奇数的情况:

      选最短的一条填{A,B},则这两点不同。

      选一条填{A,C}{B,C},另一条填{B,C},{A,C},所以开头无论填A还是B,都可以通过这两条路径限制结尾不能填A和B。

      对于三条路径都是偶数的情况:

      选最短的一条填{A,B},则这两点相同。

      另外两条填{A,C}{C,B}{B,A}/{B,C}{C,A}{A,B},同上可以限制结尾不能填A和B。

      但是另外两条路基如果有一条长度为2,则构造不出。

      综上,证明完毕。

    代码

    #include<iostream>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int N=4e4+10;
    struct edge{int next,to;}a[N];
    int n,m,head[N],cnt,del[N],col[N],flag,tt,sn,sm,p1,p2,du[N];
    queue<int> Q;
    void link(int x,int y) {a[++cnt]=(edge){head[x],y};head[x]=cnt;}
    void dfs(int x,int c)
    {
    	col[x]=c;sn++;if(flag) return;
    	int son=0;
    	for(int i=head[x];i;i=a[i].next)
    	{
    		int R=a[i].to;if(del[R]) continue;
    		sm++;son++;
    		if(col[R]&&col[R]!=3-c) {flag=1;return;}
    		if(!col[R]) dfs(R,3-c);
    	}
    	if(son==3) p1?p2=x:p1=x;
    }
    void calc(int x,int fr,int s)
    {
    	if(x==p2) {tt+=(s==2);return;}
    	for(int i=head[x];i;i=a[i].next)
    		if(!del[a[i].to]&&a[i].to!=fr)
    			calc(a[i].to,x,s+1);
    }
    void Work()
    {NO
    	memset(du,0,sizeof(du));
    	memset(head,0,sizeof(head));
    	memset(col,0,sizeof(col));
    	memset(del,0,sizeof(del));
    	flag=0;cnt=0;
    	cin>>n>>m;
    	for(int i=1,x,y;i<=m;i++)
    		scanf("%d%d",&x,&y),du[x]++,du[y]++,link(x,y),link(y,x);
    	for(int i=1;i<=n;i++) if(du[i]==1) Q.push(i);
    	while(!Q.empty())
    	{
    		int x=Q.front();Q.pop();del[x]=1;
    		for(int i=head[x];i;i=a[i].next)
    			if(--du[a[i].to]==1) Q.push(a[i].to);
    	}
    	for(int i=1;i<=n;i++)
    		if(!col[i]&&!del[i])
    		{
    			sn=sm=p1=p2=0;dfs(i,1);sm/=2;
    			if(sm>=sn+2||flag) return (void)puts("NO");
    			if(sm==sn+1) {calc(p1,0,tt=0);if(tt<2) return (void)puts("NO");}
    		}
    	puts("YES");
    }
    int main() {int T;cin>>T;while(T--) Work();}
    

    D1T3 求和

    题意

    master 对树上的求和非常感兴趣。他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的 (k) 次方和,而且每次的 (k) 可能是不同的。此处节点深度的定义是这个节点到根的路径上的边数。
    他把这个问题交给了 pupil,但 pupil 并不会这么复杂的操作,你能帮他解决吗?

    对于 (30\%) 的数据,(1 leq n,m leq 100)

    对于 (60\%) 的数据,(1 leq n,m leq 1000)

    对于 (100\%) 的数据,(1 leq n,m leq 300000,1 leq k leq 50)

    题解

    送分题。

    代码

    #include<iostream>
    using namespace std;
    const int N=3e5+10,mod=998244353;
    struct edge{int next,to;}a[N<<1];
    int n,q,fa[20][N],cnt,head[N],dep[N],val[N][51];
    void link(int x,int y) {a[++cnt]=(edge){head[x],y};head[x]=cnt;}
    void dfs(int x,int fr)
    {
    	dep[x]=dep[fr]+1;val[x][0]=1;
    	for(int i=head[x];i;i=a[i].next)
    		if(a[i].to!=fr) fa[0][a[i].to]=x,dfs(a[i].to,x);
    }
    void sum(int x,int fr)
    {
    	for(int i=1;i<=50;i++) (val[x][i]+=val[fr][i])%=mod;
    	for(int i=head[x];i;i=a[i].next) if(a[i].to!=fr) sum(a[i].to,x);
    }
    int main()
    {
    	cin>>n;
    	for(int i=1,x,y;i<n;i++)
    		scanf("%d%d",&x,&y),link(x,y),link(y,x);
    	dep[0]=-1;dfs(1,0);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=50;j++)
    			val[i][j]=1ll*val[i][j-1]*dep[i]%mod;
    	sum(1,0);
    	for(int p=1;p<=18;p++)
    		for(int i=1;i<=n;i++)
    			fa[p][i]=fa[p-1][fa[p-1][i]];
    	for(cin>>q;q;q--)
    	{
    		int x,y,k,xx,yy,lca,ans;
    		scanf("%d%d%d",&x,&y,&k);xx=x,yy=y;
    		if(dep[x]<dep[y]) swap(x,y);
    		for(int p=18;p>=0;p--)
    			if(dep[fa[p][x]]>=dep[y]) x=fa[p][x];
    		for(int p=18;p>=0;p--)
    			if(fa[p][x]!=fa[p][y])
    				x=fa[p][x],y=fa[p][y];
    		lca=(x==y?x:fa[0][x]);
    		ans=((val[xx][k]+val[yy][k])%mod-(val[lca][k]+val[fa[0][lca]][k])%mod)%mod;
    		printf("%d
    ",(ans+mod)%mod);
    	}
    }
    

    D2T1 双人猜数游戏

    题意

    Alice 和 Bob 是一对非常聪明的人,他们可以算出各种各样游戏的最优策略。现在有个综艺节目《最强大佬》请他们来玩一个游戏。主持人写了三个正整数 (s)(m)(n) ,然后一起告诉 Alice 和 Bob (s leq m leq n) 以及 (s) 是多少。(即,(s) 是接 下来要猜的 (m)(n) 的下限。)之后主持人单独告诉 Alice (m)(n) 的乘积是多少, 单独告诉 Bob (m)(n) 的和是多少。

    当然,如果一个人同时知道 (m)(n) 的乘积以及 (m)(n) 的和话就能很容易地算出 (m)(n) 分别是多少,但现在 Alice 和 Bob 只分别知道其中一个,而且只分别知道其中一个,而且他们只能回答主持人的问题,不能交流。从 Alice 或 Bob(见输入)开始 依次询问 Alice/Bob 知不知道 (m)
    (n) 分别是多少, Alice/Bob 只能回答知道/不知道。

    为了节目效果,为了显示出 Alice 和 Bob 非常聪明,主持人希望 Alice 和 Bob 一共说了 (t) 次“不知道 ”以后两个人都知道 (m)(n) 是多少了 。现在主持人找到你,希望让帮他构造一组符合条件的 (m)(n)

    对于 (40\%) 的数据, (t = 2)

    对于 (100\%) 的数据, (1 leq s leq 200)(2 leq t leq 15),输入数据保证有解。

    题解

    神仙提交答案题。

    他们的思维方式见:https://www.luogu.org/problemnew/solution/P4459

    然后就可以设计(dp[i][j][k])表示两个数字分别为(i,j),进行了(k)轮,是否已经确定了。

    然后按照两人的思维方式进行转移。有点毒瘤啊。

    代码

    一秒之内可以跑出一个点。

    #include<iostream>
    #include<cmath>
    using namespace std;
    const int N=300;
    int f[N+1][N+1][20];
    int s,t;
    string S;
    int calc1(int x,int y,int k)
    {
    	int num=x*y,up=sqrt(x*y),xx=0,yy=0,cnt=0;
    	for(int i=s;i<=up;i++)
    		if(num%i==0&&(!k||!f[i][num/i][k-1]))
    			xx=i,yy=num/i,cnt++;
    	return cnt==1&&xx==x&&yy==y;
    }
    int calc2(int x,int y,int k)
    {
    	int num=x+y,up=num/2,xx=0,yy=0,cnt=0;
    	for(int i=s;i<=up;i++)
    		if(!k||!f[i][num-i][k-1])
    			xx=i,yy=num-i,cnt++;
    	return cnt==1&&xx==x&&yy==y;
    }
    int calc3(int x,int y)
    {
    	int num=x*y,up=sqrt(x*y),xx=0,yy=0,cnt=0;
    	for(int i=s;i<=up;i++)
    		if(num%i==0&&f[i][num/i][t]&&(t<2||!f[i][num/i][t-2]))
    			xx=i,yy=num/i,cnt++;
    	return cnt==1&&xx==x&&yy==y;
    }
    int calc4(int x,int y)
    {
    	int num=x+y,up=num/2,xx=0,yy=0,cnt=0;
    	for(int i=s;i<=up;i++)
    		if(f[i][num-i][t]&&(t<2||!f[i][num-i][t-2]))
    			xx=i,yy=num-i,cnt++;
    	return cnt==1&&xx==x&&yy==y;
    }
    void Work(int x,int y)
    {
    	if(!f[x][y][t]) return;
    	for(int k=0;k<t;k++) if(f[x][y][k]) return;
    	int nw=((t&1)&&S[0]=='A')||(!(t&1)&&S[0]=='B');
    	int fl=nw?calc3(x,y):calc4(x,y);
    	if(fl) printf("%d %d
    ",x,y),exit(0);
    }
    int main()
    {
    /*	
    	freopen("makeout.in","r",stdin);
    	string fin;cin>>fin;
    	freopen(("guess"+fin+".in").c_str(),"r",stdin);
    	freopen(("guess"+fin+".out").c_str(),"w",stdout);
    */
    	cin>>s>>S>>t;
    	for(int k=0,nw=S[0]=='A';k<=t;k++,nw^=1)
    		for(int i=s;i<=N;i++)
    			for(int j=i;j<=N;j++)
    			{
    				if(k>1) f[i][j][k]=f[i][j][k-2];
    				f[i][j][k]|=nw?calc1(i,j,k):calc2(i,j,k);
    			}
    	for(int sum=2*s;;sum++)
    		for(int i=s;i<=sum/2;i++)
    			Work(i,sum-i);
    }
    

    D2T2 链上二次求和

    题意

    有一条长度为 (n) 的链( (forall 1 leq i < n) ,点 (i) 与点 (i+1) 之间有一条边的无向图), 每个点有一个整数权值,第 (i) 个点的权值是 (a_i) 。现在有 (m) 个操作,每个操作如下:

    操作 1(修改):给定链上两个节点 (u)(v) 和一个整数 (d),表示将链上 (u)(v) 唯一的简单路径上每个点权值都加上 (d)

    操作 2(询问):给定两个正整数 (l)(r),表示求链上所有节点个数大于等于 (l) 且小于等于 (r) 的简单路径节点权值和之和。由于答案很大,只用输出对质数 (1000000007) 取模的结果即可。

    一条节点个数为 (k) 的简单路径节点权值和为这条上所有 (k) 个节点(包括端点)的权值之和,而本题中要求是对所有满足要求的简单路径,求这一权值和的和。

    由于是无向图,路径也是无向的,即点 (1) 到点 (2) 的路径与点 (2) 到点 (1) 的路径是同一条,不要重复计算。

    记操作 1(修改)的次数为 (m^prime)

    对于全部数据, 保证 (n leq 200000, m leq 500000, m^prime leq 100000, 0 leq a_i < 1000000007)

    (1 leq u leq n, 1leq v leq n, 0 leq d < 1000000007, l leq r leq n)

    题解

    设S为权值前缀和,答案为$$sum_{i=l}^{r}sum_{j=i}^n(S_j-S_{j-i})=sum_{i=l}^{r}(sum_{j=i}^{n}S_j-sum_{j=0}^{n-i}S_j)=sum_{i=l}^{r}(SS_n-SS_{i-1}-SS_{n-i})$$,其中SS为S的前缀和。

    所以就是用线段树动态维护二维前缀和。

    考虑加[l,r]对二维前缀和造成的影响:

    • (lle ile r)(+=frac{(i-l+1)(i-l+2)}{2})
    • (r< i),+=(frac{(r-l+1)(r-l+2)}{2}+(r-i)(r-l+1))

    所以维护二次函数就好了。

    代码

    #include<iostream>
    using namespace std;
    const int N=8e5+10,mod=1e9+7,inv2=500000004,inv6=166666668;
    int n,m,a[N],b[N],c[N],t[N];
    int S(int n) {return 1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod;}
    void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;}
    void put(int x,int l,int r,int A,int B,int C)
    {
    	int s0=r-l+1,s1=1ll*(l+r)*(r-l+1)/2%mod;
    	int s2=(S(r)-S(l-1)+mod)%mod;
    	add(t[x],1ll*A*s2%mod);
    	add(t[x],1ll*B*s1%mod);
    	add(t[x],1ll*C*s0%mod);
    	add(a[x],A);add(b[x],B);add(c[x],C);
    }
    void pushdown(int x,int l,int r)
    {
    	if(!(a[x]+b[x]+c[x])) return;
    	int mid=(l+r)>>1;
    	put(x<<1,l,mid,a[x],b[x],c[x]);
    	put(x<<1|1,mid+1,r,a[x],b[x],c[x]);
    	a[x]=b[x]=c[x]=0;
    }
    void Add(int x,int l,int r,int gl,int gr,int a,int b,int c)
    {
    	if(l>=gl&&r<=gr) return (void)put(x,l,r,a,b,c);
    	int mid=(l+r)>>1;
    	pushdown(x,l,r);
    	if(gl<=mid) Add(x<<1,l,mid,gl,gr,a,b,c);
    	if(gr>mid) Add(x<<1|1,mid+1,r,gl,gr,a,b,c);
    	t[x]=(t[x<<1]+t[x<<1|1])%mod;
    }
    int Query(int x,int l,int r,int gl,int gr)
    {
    	if(l>=gl&&r<=gr) return t[x];
    	int mid=(l+r)>>1,res=0;
    	pushdown(x,l,r);
    	if(gl<=mid) res+=Query(x<<1,l,mid,gl,gr);
    	if(gr>mid) res+=Query(x<<1|1,mid+1,r,gl,gr);
    	return res%mod;
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1,x,s=0,ss=0;i<=n;i++)
    		scanf("%d",&x),add(s,x),add(ss,s),Add(1,0,n,i,i,0,0,ss);
    	for(int i=1;i<=m;i++)
    	{
    		int op,l,r,v;scanf("%d%d%d",&op,&l,&r);
    		if(l>r) swap(l,r);
    		if(op==1)
    		{
    			scanf("%d",&v);v=1ll*v*inv2%mod;
    			int a=v,b=(1ll*(3-2*l)*v%mod+mod)%mod;
    			int c=(1ll*v*(1ll*l*l%mod-3ll*l+2)%mod+mod)%mod;
    			Add(1,0,n,l,r,a,b,c);
    			a=0;b=2ll*(r-l+1)*v%mod;
    			c=(1ll*(r-l+1)*(r-l+2)%mod*v%mod-1ll*r*b%mod+mod)%mod;
    			if(r!=n) Add(1,0,n,r+1,n,a,b,c);
    		}
    		else
    		{
    			int ans=1ll*Query(1,0,n,n,n)*(r-l+1)%mod;
    			add(ans,mod-Query(1,0,n,l-1,r-1));
    			add(ans,mod-Query(1,0,n,n-r,n-l));
    			printf("%d
    ",ans);
    		}
    	}
    }
    

    D2T3 治疗之雨

    题意

    (没玩过《炉石传说》的人可以跳过这一段)今天我们来探讨下《炉石传说》中“治疗之雨”(恢复 (12) 点生命值,随机分配到所有友方角色上)和“暗影打击装甲”(每当一个角色获得治疗,便对随机敌人造成 (1) 点伤害)这两张卡牌之间的互动效果。假设你场上有 (m) 个剩余生命值无限大且生命值上限减去剩余生命值也无限大的随从,而对方的场上有 (k) 个暗影打击装甲,你的英雄剩余生命值为 (p) 、生命值上限为 (n) ,现在你使用了一张可以恢复无限多(而不是 (12) 点)生命值的治疗之雨,问治疗之雨期望总共恢复了几点生命值以后你的英雄会死亡(生命值降为 (0) ;治疗之雨的判定机制使得在此后再也不会为英雄恢复生命值)。

    注:题目背景与题目描述有冲突的地方请以题目描述为准

    下面让我们再形式化地描述一遍问题。

    你现在有 (m+1) 个数:第一个为 (p) ,最小值为 (0) ,最大值为 (n) ;剩下 (m) 个都是无穷,没有最小值或最大值。你可以进行任意多轮操作,每轮操作如下:

    在不为最大值的数中等概率随机选择一个(如果没有则不操作),把它加一;

    进行 (k) 次这个步骤:在不为最小值的数中等概率随机选择一个(如果没有则不操作),把它减一。

    现在问期望进行多少轮操作以后第一个数会变为最小值 (0)

    对于 (10\%) 的数据, (n leq 3)(m, k leq 2)

    对于 (20\%) 的数据, (n, m, k leq 5)

    对于 (30\%) 的数据, (n, m, k leq 30)

    对于 (40\%) 的数据, (n, m, k leq 50)

    对于 (50\%) 的数据, (n, m, k leq 200)

    对于 (70\%) 的数据, (n leq 200)

    对于 (80\%) 的数据, (n leq 500)

    对于 (100\%) 的数据, (1 leq T leq 100)(1 leq p leq n leq 1500)(0 leq m, k leq 1000000000)

    //保证不存在 (n=p=k=1)(m=0) 的情况(因为出题人判错了)

    //保证不存在答案的分母是(1000000007)的倍数的情况(因为出题人没想到)

    题解

    这题应该能自己想出来的,但是没有看懂题所以通过题解看懂了题目。。

    题意是每次给没有满血的位置+1,给没有死的位置-1。

    那么就可以高斯消元了。由于其矩阵的优美性质,可以做到(n^2)

    代码

    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=1600,mod=1e9+7;
    int n,m,p,k,rv[N],P[N],F[N][N],f[N];
    int ksm(int x,int k)
    {
    	int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
    				if(k&1) s=1ll*s*x%mod;return s;
    }
    int Work()
    {
    	memset(P,0,sizeof(P));
    	cin>>n>>p>>m>>k;
    	if(!k||(m==0&&k==1)) return -1;
    	if(m==0) {int res=0;for(;p>0;p-=k,res++) if(p<n) p++;return res;}
    	int rev=ksm(m+1,mod-2);rv[1]=1;
    	for(int i=2;i<=n+1;i++) rv[i]=mod-1ll*mod/i*rv[mod%i]%mod;
    	for(int i=0,C=1;i<=min(n,k);C=1ll*C*rv[i+1]%mod*(k-i)%mod,i++)
    		P[i]=1ll*C*ksm(rev,i)%mod*ksm(1ll*m*rev%mod,k-i)%mod;
    	for(int i=1;i<n;i++)
    	{
    		for(int j=1;j<=i;j++)
    			F[i][j]=(1ll*m*rev%mod*P[i-j]%mod+1ll*rev*P[i-j+1]%mod)%mod;
    		F[i][i+1]=1ll*rev*P[0]%mod;
    		(F[i][i]+=mod-1)%=mod;
    		F[i][n+1]=mod-1;
    	}
    	for(int i=1;i<=n;i++) F[n][i]=P[n-i];
    	(F[n][n]+=mod-1)%=mod;F[n][n+1]=mod-1;
    
    	for(int i=n;i>=2;i--)
    	{
    		if(!F[i][i]) return -1;
    		int t=1ll*F[i-1][i]*ksm(F[i][i],mod-2)%mod;
    		for(int j=i;j>=1;j--) F[i-1][j]=(F[i-1][j]-1ll*F[i][j]*t%mod+mod)%mod;
    		F[i-1][n+1]=(F[i-1][n+1]-1ll*F[i][n+1]*t%mod+mod)%mod;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		int res=F[i][n+1];
    		for(int j=1;j<i;j++) res=(res-1ll*f[j]*F[i][j]%mod+mod)%mod;
    		f[i]=1ll*res*ksm(F[i][i],mod-2)%mod;
    	}
    	return f[p];
    }
    int main() {int T;cin>>T;while(T--) printf("%d
    ",Work());}
    

    后记

    这一年的BJOI出得很好啊。但是自己只能写出来D1T1T3、D2T3,而且T1还不一定能调对。

    菜是原罪啊。。。HNOI2019加油啊!

  • 相关阅读:
    网络对抗技术 20181216 Exp6 MSF基础应用
    实验一-密码引擎-加密API研究
    网络对抗技术 20181216 Exp5 信息搜集与漏洞扫描
    网络对抗技术 20181216 Exp4 恶意代码分析原理与实践说明
    网络对抗技术 20181216 Exp3 免杀原理与实践
    用Visual Studio 2019 创建C#窗体项目
    EL表达式的学习
    session学习
    mysql+javaWeb+jdbc+tomcat开发中的中文乱码处理
    java集合
  • 原文地址:https://www.cnblogs.com/xzyxzy/p/10656803.html
Copyright © 2011-2022 走看看