zoukankan      html  css  js  c++  java
  • 【BZOJ4872】【Shoi2017】分手是祝愿

    Time Limit: 20 Sec Memory Limit: 512 MB

      

    Description

      
    ​   Zeit und Raum trennen dich und mich.
      
    ​   时空将你我分开。B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。
      

    Input

        
    ​   第一行两个整数 n, k。
      
    ​   接下来一行 n 个整数,每个整数是 0 或者 1,其中第 i 个整数表示第 i 个灯的初始情况。
          
      ​ 1 ≤ n ≤ 100000, 0 ≤ k ≤ n;
      

    Output

      
    ​   输出一行,为操作次数的期望乘以 n 的阶乘对 100003 取模之后的结果。
      

    Sample Input

       
    ​   4 0
    ​   0 0 1 1
      

    Sample Output

      
    ​   512
      
      
      

    Solution

      
    ​   我开始想到生成函数什么的东西去了,然而这题隐藏得是在太深了......
      
    ​   首先考虑没有随机时的最优策略:由于一个开关只能影响到比它编号小的灯,所以我们从大到小考虑,如果当前灯是亮的,我们必须要动一次开关,并更新编号为其约数的电灯。显然这是唯一且最优策略
      
    ​   考虑这个策略,记全部开关为(A),按过的开关集合为(S),我们发现每个(S)中的开关必须按且只按一次,而(A-S)中的开关一次不按。这样一来后面的问题就根本不涉及什么约数问题了,问题变为:按多少次才能将(S)中的开关按奇数次,将(A-S)中的开关按偶数次。
      
    ​   记(f_i)表示还需要连续按对(i)次才能达到最终状态,转移到(f_{i-1})的期望步数。“按对”指的是将(S)中还是偶数次的开关按下,或者将(A-S)中还是奇数的开关按下。易得:
      

    [egin{aligned} f_i&=frac in*1+frac{n-i}n(1+f_{i+1}+f_i)\ &=frac{(n-i)f_{i+1}+n}i end{aligned} ]

      
    ​   作为边界条件,(f_n=1),因为随便按一个都是往正确方向走的(太差了)。
      
    ​   所以得到(ans=k+sum_{i=k+1}^{need}f_i),其中(need)是最优策略步数。复杂度(O(n;log;n))
      
    ​   这题的关键在于化简题目,找到本质,然后就发现这是一道经典的简单的期望DP。=_=
      

    #include <cstdio>
    #include <vector>
    #define pb push_back
    using namespace std;
    const int N=100005,MOD=100003;
    int n,K,a[N],need;
    int inv[N],factn;
    int f[N];
    vector<int> d[N];
    int main(){
    	freopen("input.in","r",stdin);
    	scanf("%d%d",&n,&K);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	factn=1;
    	for(int i=2;i<=n;i++) factn=1LL*factn*i%MOD;
    	inv[1]=1;
    	for(int i=2;i<=n;i++)
    		inv[i]=-1LL*(MOD/i)*inv[MOD%i]%MOD;
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j+=i)
    			d[j].pb(i);
    	need=0;
    	for(int i=n;i>=1;i--)
    		if(a[i]){
    			need++;
    			for(int j=0,sz=d[i].size();j<sz;j++)
    				a[d[i][j]]^=1;
    		}
    	if(need<=K){printf("%d
    ",1LL*need*factn%MOD);return 0;}
    	f[n]=1;
    	for(int i=n-1;i>K;i--)
    		f[i]=(1LL*(n-i)*f[i+1]%MOD+n)*inv[i]%MOD;
    	int ans=K;
    	for(int i=K+1;i<=need;i++) (ans+=f[i])%=MOD;
    	ans=1LL*ans*factn%MOD;
    	printf("%d
    ",ans<0?ans+MOD:ans);
    	return 0;
    }
    
  • 相关阅读:
    十三 web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息
    十二 web爬虫讲解2—Scrapy框架爬虫—Scrapy模拟浏览器登录—获取Scrapy框架Cookies
    十一 web爬虫讲解2—Scrapy框架爬虫—Scrapy使用
    十 web爬虫讲解2—Scrapy框架爬虫—Scrapy安装—Scrapy指令
    九 web爬虫讲解2—urllib库爬虫—实战爬取搜狗微信公众号—抓包软件安装Fiddler4讲解
    八 web爬虫讲解2—urllib库爬虫—ip代理—用户代理和ip代理结合应用
    七 web爬虫讲解2—urllib库爬虫—状态吗—异常处理—浏览器伪装技术、设置用户代理
    六 web爬虫讲解2—urllib库爬虫—基础使用—超时设置—自动模拟http请求
    五 web爬虫,scrapy模块,解决重复ur——自动递归url
    四 web爬虫,scrapy模块标签选择器下载图片,以及正则匹配标签
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/8880131.html
Copyright © 2011-2022 走看看