zoukankan      html  css  js  c++  java
  • noip模拟赛#38

    我打开了#39的problem。。。想了半个小时多发现我一道题都不会写。。。于是我打开了#38的problem

    T1:循环数字的定义为能够将该数划分为若干相同长度的段并且都相同。 n=2e18.

    =>我只会70%的暴力。。。

    正解:

        对于[L,R]内的循环整数个数,可以看成是[1,R]内的循环整数个数减去[1,L-1]内的循环整数个数。

        对于确定了循环节长度i以及数字长度n的循环整数,在[1,x]内的个数可以用MAX{x div k-10^(i-1)+1,0}算出,其中k是i-1个0,1个1,循环n div i次所得的数字。

        例如,若求[1,666666]内长度为6的循环节长度为3的数字只需要用666666 div 1001-100+1=567.如此即可快速算出数字个数。这个公式成立当且仅当x的数字长度等于n。也就是说[1,666666]内长度为5的循环节长度为1的数字等于99999 div 11111-1+1=9.不能使用666666作为被除数。

        所以就可以枚举循环节长度以及数字长度,利用以上公式算出循环整数的数字个数。

       但是,很容易发现,这种做法会引起重复计算。因为如果一个整数的循环节长度是i,那只要k*i是数字长度的约数,k*i也是该整数的循环节长度,所以每次计算循环节长度为i的循环整数个数的时候也要把循环节长度为i的约数的所有合法循环整数减去。

       也就是说,9999在循环节长度是1的时候已经算了一遍,而在循环节长度为2的时候也会算一次,所以要减去。

       时间复杂度为O(T*(lgMAX{R})^2),期望得分100%。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define clr(x,c) memset(x,c,sizeof(x))
    int read(){
    	int x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	return x;
    }
    const int nmax=2e6+5;
    const int inf=0x7f7f7f7f;
    int a[nmax],be[10];
    bool pd(int x){
    	int cnt=0;while(x) be[++cnt]=x%10,x/=10;
    	if(cnt%2||cnt==2){
    		rep(i,2,cnt) if(be[i]!=be[i-1]) return 0;
    		return 1;
    	}else{
    		if(cnt==4){
    			if(be[1]==be[3]&&be[2]==be[4]) return 1;
    			return 0;
    		}else{
    			if(be[1]==be[3]&&be[1]==be[5]&&be[2]==be[4]&&be[2]==be[6]) return 1;
    			if(be[1]==be[4]&&be[2]==be[5]&&be[3]==be[6]) return 1;
    			return 0;
    		}
    	}
    }
    int main(){
    	freopen("circulate.in","r",stdin);freopen("circulate.out","w",stdout);
    	rep(i,11,nmax-1) a[i]=a[i-1]+pd(i);
    	int n=read(),u,v;
    	rep(i,1,n){
    		u=read(),v=read();
    		printf("%d
    ",a[v]-a[u-1]);
    	}
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    T2:求多少个长度为n的严格单调递增或严格单调递减的数列。数列的数字[1,n]。n=1e6+5;mod=2e6+3;

    =>打表可以发现规律。ans=C(n-1,n-1-n+1)+C(n,n-n+1)+···+C(2*n-2,2*n-2-n+1)。ans=ans*2-n。看出这个之后线性求逆元就可以了。

    正解:

           我们可以想象,如果要数列不递增或不递减(不考虑只有一种数的情况)那么就可以选出若干个数再排序,所以是一个组合题,不是排列题。

           然后我们可以在n-1个空里面放上i个挡板,没有放挡板的位置代表其相邻两个数相等。

    如1 / 2,2,2 / 3,就是一个只放了2个挡板的序列,于是我们可以在n-1个数里面取i个位置放挡板,然后从N个数里面取出i个数放进去ANS=sigma{C(N-1,i-1)*C(N,i),1<=i<=N},由于两个组合数确定了一个参数,另一个参数几乎都需要用到,我们可以使用递推。

           C(N,0)=1;  C(N,i)=C(N,i-1)*(n-i) div (i+1) 于是就可以递推出所有C(N,i),C(N-1,i)

           由于有求余操作,我们可以使用逆元,实现div。

           时间复杂度为O(NlogN),期望得分100%。

    好神啊。。。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define clr(x,c) memset(x,c,sizeof(x))
    #define ll long long
    int read(){
    	int x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	return x;
    }
    const int nmax=2e6+5;
    const int inf=0x7f7f7f7f;
    const int mod=2e6+3;
    int inv[mod+1],fac[nmax];
    int main(){
    	freopen("array.in","r",stdin);freopen("array.out","w",stdout);
    	inv[1]=1;
    	rep(i,2,mod) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    	int n=read();
    	fac[1]=1;rep(i,2,n+n) fac[i]=1ll*fac[i-1]*i%mod;
    	int ans=1;
    	rep(i,2,n) ans=(ans+1ll*fac[n+i-2]*inv[fac[i-1]]%mod*inv[fac[n-1]]%mod)%mod;
    	printf("%d
    ",(ans+ans-n+mod)%mod);
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    T3:给出一个长度为n的数列。数列中的数[1,n]。求最多能够组成多少个长度为3的严格上升序列。

    =>想啊想想啊想。。。想他的性质的时候突然想到数的大小是没有用的。。。关键是统计每个数字有多少个后搞。然后贪心。先尽量去那些个数多的。那么弄三个指针扫一下就好了。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define clr(x,c) memset(x,c,sizeof(x))
    int read(){
    	int x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	return x;
    }
    const int nmax=3e6+5;
    const int inf=0x7f7f7f7f;
    int a[nmax];int vis[nmax];
    int main(){
    	freopen("cakes.in","r",stdin);freopen("cakes.out","w",stdout);
    	int n=read(),u,cnt=0;
    	rep(i,1,n){
    		u=read();
    		if(!vis[u]) vis[u]=++cnt;
    		a[vis[u]]++;
    	}
    	sort(a+1,a+cnt+1);
    	//rep(i,1,cnt) printf("%d ",a[i]);printf("
    ");
    	int ca=cnt-2,cb=cnt-1,cc=cnt,ans=0,tmp;
    	while(ca>0&&cb>0&&cc>0&&ca<cb&&cb<cc){
    		//printf("%d %d %d
    ",ca,cb,cc);
    		++ans;--a[ca];--a[cb];--a[cc];
    		if(!a[cc]){
    			tmp=cb,cb=ca,--ca,cc=tmp;
    			if(!a[cc]) tmp=cb,cb=ca,--ca,cc=tmp;
    			if(!a[cc]) tmp=cb,cb=ca,--ca,cc=tmp;
    		}
    		if(!a[cb]) {
    			cb=ca,--ca;
    			if(!a[cb]) cb=ca,--ca;
    		}
    		if(!a[ca]) --ca;
    	}
    	printf("%d
    ",ans);
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    /*
    6
    1 2 3 4 3 2
    6
    1 1 1 2 2 3
    12
    1 1 2 5 8 2 2 2 3 3 3 1 3
    */
  • 相关阅读:
    POJ-1189 钉子和小球(动态规划)
    POJ-1191-棋盘分割(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
  • 原文地址:https://www.cnblogs.com/fighting-to-the-end/p/5994792.html
Copyright © 2011-2022 走看看