OI时期其他题解

JZYZP1815

一道贪心,喵?

好吧其实就是个贪心,最大值比较简单,最小值不太好搞看看代码自己理解

#include <bits/stdc++.h>
#define LL long long
using namespace std;


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x;
}

int main()
{
    LL n , x , y , k , xx , yy , w;
    LL ans ;
    while( scanf( "%lld" , & x ) == 1 )
    {
        xx = x , y = yy = read() , n = read();
        //特判
        if( n == 1 )
        {
            if( x > y ) puts("3 3");
            else if( x == y ) puts("1 1");
            else puts("0 0");
            continue;
        }
        //max
        w = n - 1;
        k = min( n - 1 , x ) , x -= k , w -= k , ans = k * 3 ;
        if( x > y ) ans += 3;
        else if( x == y ) ans += 1;
        ans += w;
        printf("%lld " , ans );
        //min
        if( xx > yy )
        {

            n -- , ans = 3 ; //把所有的 x 都放到一个格子里,保证只赢一次
            k = min( yy , n ) , ans += n - k; //再给剩下的格子尽可能的放 y
            printf( "%lld\n" , ans );
            continue;
        }

        if( xx == yy )
        {
            if( n >= 3 && yy >= 3 )
            {
                // 同 xx > yy
                n -- , ans = 3 ;
                k = min( yy , n ) , ans += n - k;
                printf( "%lld\n" , ans );
            }
            else printf( "%lld\n" , n ); //直接全部平局
            continue;
        }

        // 对于xx<yy的情况
        n -- ;
        ans = 3 + n - ( min( n , yy ) ); // 把所有的 x 放在一个格子里,剩下的格子每个放一个 y
        yy -= xx;
        ans = min( ans , 1 + n - min( n , yy ) ); // 在一个格子里抵消掉所有的 x 后面的全部放 y
        yy --;
        ans = min( ans , n - min( n , yy ) ); // 在保证不会赢的情况下,尽可能的放 y
        printf( "%lld\n" , ans );
    }
    return 0;
}

JZYZP1810

这就是道数据结构模板题,方法很多我用是分块

#include<bits/stdc++.h>
#define L( x ) ( ( x - 1 ) * len + 1 )
#define R( x ) ( x * len )
#define pos( x ) ( ( x - 1 ) / len + 1 )
using namespace std;


const int N = 100000 , M = 1000;
int n , m , len , pos[N] , last , gp[M];
int tag[M] , v[N];


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x;
}


int main()
{
    n = read() , last = m = read() , len = sqrt( 1.0 * m );
    for( register int i = 1 ; i <= m ; gp[ pos(i) ] ++ , v[i] = 1 , i ++ );
    for( register int l , r , p , q , sum ; n >= 1 ; n -- )
    {
        l = read() , r = read() , p = pos( l ) , q = pos( r ) , sum = 0;
        if( last == 0 )
        {
            puts("0");
            continue;
        }
        if( p == q )
        {
            for( register int i = l ; i <= r ; i ++ )
            {
                if( tag[p] ) break;
                sum += v[i] , gp[q] -= v[i] , v[i] = 0;
            }
            printf( "%d\n" , last - sum );
            last -= sum ;
            continue;
        }
        for( register int i = l ; i <= R( p ) && !tag[p] ; sum += v[i] , gp[p] -= v[i] , v[i] = 0 , i ++ );
        for( register int i = r ; i >= L( q ) ; i -- )
        {
            if( tag[q] ) break;
            sum += v[i] , gp[q] -= v[i] , v[i] = 0;
        }
        for( register int i = p + 1 ; i <= q - 1 ; i ++ )
        {
            if( tag[i] ) continue;
            sum += gp[i] , tag[i] = 1;
        }
        printf( "%d\n" , last - sum );
        last -= sum;
    }
}

JZYZP1993

显然是道树状数组的题目,区间修改,单点查询,维护一个差分序列

本题主要有两个坑点

  1. 下标从\(0\)开始,树状数组不能维护从\(0\)开始的下标,所以要全部加一
  2. 卡输入输出,喵呀
#include <bits/stdc++.h>
#define lowbit( x ) ( x & -x )
using namespace std;


const int N = 1000005;
int n , m , bit[N];
char buf[ 1 << 15 ] , * fs , * ft ;


inline char getc(){ return ( fs == ft && ( ft = ( fs = buf ) + fread( buf , 1 , 1 << 15 , stdin ) , fs == ft ) ) ? 0 : * fs ++ ; }

inline int read()
{
    register int x = 0 , f = 1 ;
    register char ch = getc() ;
    while( !isdigit( ch ) )
    {
        if( ch == '-' )  f = -1 ;
        ch = getc();
    }
    while( isdigit( ch ))
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0' ;
        ch = getc();
    }
    return x * f;
}

inline void put( int x )
{
    if( x == 0 )
    {
        putchar( '0' );
        putchar( '\n' );
        return;
    }
    if( x < 0 )
    {
        putchar( '-' );
        x =- x;
    }
    register int num = 0;
    register char ch[16];
    while( x ) ch[ ++ num ] = x % 10 + '0' , x /= 10 ;
    while( num ) putchar(ch[num--]) ;
    putchar('\n');
}

inline int query( int x )
{
    register int sum = 0;
    for( register int i = x ; i >= 1 ; i -= lowbit( i ) ) sum += bit[i];
    return sum;
}

inline void add( int x , int w )
{
    for( register int i = x ; i <= n ; i += lowbit( i ) ) bit[i] += w;
    return ;
}


int main()
{
    n = read() + 1, m = read();
    for( register int op , l , r ; m >= 1 ; m -- )
    {
        op = read() , l = read() + 1 ;
        if( op == 1 ) r = read() + 1 , add( l , 1 ) , add( r + 1 , -1 );
        else put( query(l) );
    }
    return 0;
}

JZYZP1813

用线段树统计一下个数,最后做个除法,没啥难的,主要是要知道怎么用线段树维护一个桶吧

#include <bits/stdc++.h>
#define LL long long
#define lowbit( x ) ( x & - x )
using namespace std;


const int N = 1000010;
int n , m , a[N] , bit[N] , tp;
LL sum;

inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x;
}

inline void add( int x )
{
    for( register int i = x ; i <= tp ; bit[i] ++ , i += lowbit( i ) );
}

inline LL query( int x )
{
    register LL res = 0;
    for( register int i = x ; i >= 1 ; res += bit[i] , i -= lowbit( i ) );
    return res;
}


int main()
{
    n = read() , m = read();
    for( register int i = 1 ; i <= n ; a[i] = read() , tp = max( tp , a[i] ) , i ++ );
    for( register int i = 1 ; i <= n ; add( a[i] ) , i ++ );
    for( register int i = 1 ; i <= n ; i ++ )
    {
        sum += query( m - a[i] );
        if( a[i] <= m - a[i] ) sum --;
    }
    register double ans = (double)sum / (double)( n * n - n );
    printf( "%.2lf\n" , ans );
    return 0;
}

JZYZP1280

都说这是到动态规划,当我不想用动态规划,于是就有了搜索的做法

其实找一份背包的代码,你会发现原理都差不多

y > m时,ans = max( ans , cnt - 1 )的原因时此时使用了y + 1CD所以要减去一个,其他真没啥难度了

#include <bits/stdc++.h>
using namespace std;

const int N = 25;
int n , m , t , w[N] , ans;


inline void dfs( int x , int y , int nt ,  int cnt )
{
    if( t < w[x] )
    {
        dfs( x + 1 , y , nt , cnt );
        return ;
    }
    if( y > m  )
    {
        ans = max( cnt - 1 , ans );
        return ;
    }
    if( x > n )
    {
        ans = max( cnt , ans);
        return ;
    }
    ans = max( cnt , ans );
    if( w[x] + nt <= t ) dfs( x + 1 , y , nt + w[x] , cnt + 1 );
    else dfs( x + 1 , y + 1 , w[x] , cnt + 1 );
    dfs( x + 1 , y , nt , cnt );
    return ;
}

int main()
{
    cin >> n >> t >> m ;
    for( register int i = 1 ; i <= n ; i ++ ) cin >> w[i];
    dfs( 1 , 1 , 0 , 0 );
    cout << ans << endl;
    return 0;
}

JZYZP1262

线性\(dp\),lis问题的四种情况,考码力和细节吧

#include <bits/stdc++.h>
using namespace std;

const int N = 7e5 + 5;
int n , a[N] , q[N] , tot ;


inline int read()
{
    register int x = 0 , f = 1;
    register char ch = getchar();
    while( ch < '0' || ch > '9' )
    {
        if( ch == '-' ) f = -1;
        ch = getchar();
    }
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x * f;
}


int main()
{
    n = read();
    for( register int i = 1 ; i <= n ; a[i] = read() , i ++ );

    // 最长上升序列长度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] > q[ tot ] ) q[ ++ tot ] = a[i];
        else *lower_bound( q + 1 , q + 1 + tot , a[i] ) = a[i];
    }
    cout << tot << endl;

    // 最长下降序列长度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] < q[ tot ] ) q[ ++ tot ] = a[i];
        else *lower_bound( q + 1 , q + 1 + tot , a[i] , greater<int>() ) = a[i];
    }
    cout << tot << endl;

    // 最长不上升序列长度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] <= q[ tot ] ) q[ ++ tot ] = a[i];
        else *upper_bound( q + 1 , q + 1 + tot , a[i] , greater<int>() ) = a[i];
    }
    cout << tot << endl;

    // 最长不下降序列长度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] >= q[ tot ] ) q[ ++ tot ] = a[i];
        else *upper_bound( q + 1 , q + 1 + tot , a[i] ) = a[i];
    }
    cout << tot << endl;

}

JZYZP1448

因为是\(noip\)的原题,所以在哪都有,但我们校\(OJ\)卡常数

所以就要换一种做法

我们已知秦九韶可以\(O(n)\)的计算出每个数,那么是否可以优化这个复杂度呢

\[设原式为f(x),b=x\mod p \\ \therefore f(x)\equiv f(b) (\mod p) \\ \because f(x) \equiv 0 (\mod p) , \therefore f(b)\equiv 0(\mod p) \]

所以我们可以先预处理出\(1\dots p-1\)中所有的数,然后对于\(\forall i\in [1,m] f(i)\)我们判断\(f(i\mod p)\)即可

这样我们只需要缩小\(p\)就可以降低复杂度,但是如果\(p\)比较小就可能会出现冲突,所以我们取多个模数即可

根据实测在\(10^5\)\(4\)个模数是最优的

#include <bits/stdc++.h>
using namespace std;


const int N = 105 , M = 1e6 + 5 , p1 = 11261 , p2 = 19997 , p3 = 14843 , p4 = 22877;
int n , m , a[N] , b[N] , c[N] , d[N] , tot , ans[M];
bool f1[ p1 + 10 ] , f2[ p2 + 10 ] , f3[ p3 + 10 ] , f4[ p4 + 10 ];


inline void read( int & a , int & b , int & c , int & d )
{
	register int f = 1;
	register char ch = getchar();
	for( ; ch < '0' || ch > '9' ; ( ch == '-' ? f = - 1 : f ) , ch = getchar() );
	for( ; ch >= '0' && ch <= '9' ; a = ( ( a << 3 ) % p1 + ( a << 1 ) % p1 + ch - '0' ) % p1 , b = ( ( b << 3 ) % p2 + ( b << 1 ) % p2 + ch - '0' ) % p2  , c = ( ( c << 3 ) % p3 + ( c << 1 ) % p3 + ch - '0' ) % p3 , d = ( ( d << 3 ) % p4 + ( d << 1 ) % p4 + ch - '0' ) % p4 , ch = getchar() );
	a *= f , b *= f , c *= f , d *= f ;
	return ;
}

inline bool check( int x , int p , int *a )
{
	int s = a[n];
	for( register int i = n - 1 ; i >= 0 ; i -- ) s = ( s * x + a[i] ) % p ;
	return !s;
}

inline void work( int p , bool * f , int * a )
{
	f[0] = !a[0];
	for( register int i = 1 ; i < p ; i ++ ) f[i] = check( i , p , a );
	return ;
}


int main()
{
	cin >> n >> m;
	for( register int i = 0 ; i <= n ; read( a[i] , b[i] , c[i] , d[i] ) , i ++ );
	
	work( p1 , f1 , a ) , work( p2 , f2 , b ) , work( p3 , f3 , c ) , work( p4 , f4 , d );
	
	for( register int i = 1 ; i <= m ; i ++ )
	{
		if( f1[ i % p1 ] && f2[ i % p2 ] && f3[ i % p3 ] && f4[ i % p4 ] ) ans[ ++ tot ] = i;
	}
	
	printf( "%d\n" , tot );
	for( register int i = 1 ; i <= tot ; printf( "%d\n" , ans[i] ) , i ++ );
	return 0;
}

JZYZP1447

\(Noip\)原题,首先考虑如何满足第一个条件,反向建图,充目标点开始做\(DFS\),把所有到达的点打上标记,这样如果一个点的子节点全部都被打上标记,那么这个点就满足第一条件

第二个条件就是裸的最短路,直接在被标记的点的中跑一遍最短路

#include <bits/stdc++.h>
#define PII pair< int, int >
#define F first
#define S second
#define vit vector<int>::iterator 
using namespace std;


const int N = 10005 , INF = 0x7f7f7f7f;
int n , m , st , ed , dis[N];
bool vis[N] , can[N];
set< PII > s;
vector<int> e[N] , t[N] ;


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' )
	{
		x = ( x << 3 ) + ( x << 1 ) + ch - '0';
		ch = getchar(); 
	} 
	return x;
}

inline void add( int x , int y ) { e[x].push_back(y) , t[y].push_back(x); }

inline void dfs( int x )
{
	can[x] = 1;
	for( register vit v = t[x].begin() ; v != t[x].end() ; v ++ )
	{
		if( can[*v] ) continue; 
		dfs( *v );
	}
	return ;
}

inline bool panding( int x )
{
	for( register vit v = e[x].begin() ; v != e[x].end() ; v ++ )
	{
		if( can[*v] ) continue;
		return 1;
	}
	return 0;
}

inline void Dijkstra()
{
	for( register int i = 1 ; i <= n ; i ++ ) dis[i] = INF;
	s.insert( { 0 , st } ) , dis[st] = 0;
	
	for( register int u , w; s.size() ; )
	{
		u = s.begin() -> S , s.erase( s.begin() );
		if( vis[u] ) continue;
		vis[u] = 1;
		if( panding(u) ) continue;
		if( u == ed ) return ;
		for( register vit v = e[u].begin() ; v != e[u].end() ; v ++ )
		{
			if( dis[*v] <= dis[u] + 1 ) continue;
			dis[*v] = dis[u] + 1;
			s.insert( { dis[*v] , *v } );
		}
	}
}


int main()
{
	n = read() , m = read();
	for( register int x , y ; m >= 1 ; x = read() , y = read() , add( x , y ) , m -- );
	st = read() , ed = read();
	
	dfs(ed);
	
	Dijkstra();
	
	cout << ( dis[ed] == INF ? -1 : dis[ed] ) << endl;
	return 0;
}

JZYZP1846

一道搜索题,可以采用一个非正确的算法水过去

我们判断程序运行的时间,如果接进\(T\)的时候输出当前的最优解,然后直接结束程序

这个代码不能保证正确性,也不能保证复杂度,正确性取决于测评机是速度

但要知道在比赛中,如果数据达到一定规模,你无法继续优化复杂度的情况下,通过这种方式来偏分,对一个就是赚一个

这种方式肯定比你直接输出随机数正确性高

#include <bits/stdc++.h>
#define exmax( x , y , z ) ( max( x , max( y , z ) ) ) 
using namespace std;


const int N = 33 , INF = 0x7f7f7f7f , ed = 980;
int n , a[N] , ans = INF , t1 , t2 , sum;


inline void dfs( int t , int s1 , int s2 , int s3 )
{
	if( exmax( s1 , s2 , s3 ) >= ans ) return ;
	if( t > n )
	{
		ans = min( ans , exmax( s1 , s2 , s3 ) );
		return ;
	}
	t2 = clock();
	if( t2 - t1 >= ed )
	{
		cout << ans << endl;
		exit(0);
	}
	dfs( t + 1 , s1 + a[t] , s2 , s3 );
	dfs( t + 1 , s1 , s2 + a[t] , s3 );
	dfs( t + 1 , s1 , s2 , s3 + a[t] );
	return ;
}

int main()
{
	t1 = clock();
	cin >> n;
	for( register int i = 1 ; i <= n ; i ++ ) cin >> a[i];
	sort( a + 1 , a + 1 + n , greater<int>() );
	dfs( 1 , 0 , 0 , 0 );
	cout << ans << endl;
	return 0;
}

JZYZOJ 1629 马棚

新年第二天写了一道很有意思的题。

首先定义\(f[i][j]\)为前\(i\)头牛用了前\(j\)个牛棚的最小违和值

\(black[i]\)\(white[i]\)是前i个牛有多少白牛多少黑牛

\(k\)是前\(k\)头牛用了前\(j-1\)个牛棚

\(get(a,b)\)\(a\)\(b\)之间所有牛的违和值

所以状态转移方程为f[i][j] = min(f[k][j-1]+get(k,i))

有了状态转移方程这到题就很好写了

coding

#include <bits/stdc++.h>
using namespace std;


const int N = 505;
int n,k,black[N] = {},white[N] = {},f[N][N] = {};


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3)+(x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}

inline int get(int l,int r)
{
	return (black[r]-black[l])*(white[r]-white[l]);
}


int main()
{
	memset(f,0x7f7f7f7f,sizeof(f)); f[0][0] = 0;
	n = read(); k = read();
	for(register int i = 1;i <= n;i++)
	{
		register int x = read();
		if(x)
		{
			black[i] = black[i-1]+1;
			white[i] = white[i-1];
		}
		else
		{
			black[i] = black[i-1];
			white[i] = white[i-1]+1;
		}
	}
	for(register int j = 1;j <= k;j++)
	{
		for(register int i = j;i <= n;i++)
		{
			for(register int k = j-1;k <= i-1;k++)	
			{
				f[i][j] = min(f[i][j],f[k][j-1]+get(k,i));
			}
		}
	}
	
	printf("%d\n",f[n][k]);
	return 0;
}

HAOI 2010 订货

JZYZOJ 1327 订货

luogu P2517 订货

首先呢,看到有人说这是网络流。好吧我不会。
所以我就用常规点的方法来写吧。

很显然\(DP\),不要问我为什么,这就跟证明贪心一样。

  1. 闭上眼睛
  2. 想一想
  3. WA是对的

好了不多哔哔直接开始。

首先我们的货物不一定只能小于等于\(s\),我们进货后可以直接卖掉不进仓库

我们要求的是最小的成本,所以\(f[j]\)肯定就是存成本了。

呢么\(j\)是什么意思呢。\(j\)表示当前仓库里有多少货物。

但是你会发现没有时间对吧我们用\(i\)来表示时间

所以\(f[j]\)就表示在\(i\)月时仓库里有\(j\)个货物所需成本。

现在假设我们已知\(i-1\)月的$f[j]

并且\(i-1\)月需求量\(u[i-1]\)也是已知的

到了这个月因为有储存费所以成本会增加

所以在第\(i\)月还未进货时的成本为

f[j] = f[j+u[i-1]] + j*m;

当然仓库也是有大小的超过\(s\)的是不合法的所以在求最优解之前

for(register int j = s+1;j <= s+u[i-1];j++) f[j] = INF;

呢么\(i\)月的\(f[j]\)的最优值我们就可以求得

f[j] = min(f[j],f[j-1]+d[i];

所以我们只要从第\(1\)月一直求到第\(n\)月就好了

#include <bits/stdc++.h>
using namespace std;


const int N = 105,S = 20005,INF = 0x7f7f7f7f;
int n,m,s,u[N] = {},d[N] = {},f[S] = {};


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3)+(x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}


int main()
{
	n = read(); m = read(); s = read();
	for(register int i = 1;i <= n;i++) u[i] = read();
	for(register int i = 1;i <= n;i++) d[i] = read();
	memset(f,INF,sizeof(f));
	u[0] = f[0] = 0;
	for(register int i = 1;i <= n;i++)
	{
		for(register int j = 0;j <= s;j++) f[j] = f[j+u[i-1]] + j*m;
		for(register int j = s+1;j <= s+u[i-1];j++) f[j] = INF;
		for(register int j = 1;j <= s+u[i];j++) f[j] = min(f[j],f[j-1]+d[i]);
	}
	printf("%d\n",f[u[n]]);
	return 0;
}

[HNOI2008]越狱

退役后的第一篇题解

原题链接

某天数学课讲了一个非常类似的问题,我就思考了一下,感觉思维还没变弱

考虑容斥

首先全部的情况,对于\(n\)个屋子每个屋子有\(m\)种选择所以总的情况数为\(m^n\)

不满足条件的情况,即不会越狱的情况,首先第一个格子没有限制有\(m\)种选择,对于第二个格子不能和第一个相同故有\(m-1\)选择,第三个不能和第二个相同,但可以和第一个相同故有\(m-1\)种选择,以此类推不会发生越狱的情况有\(m\times (m-1)^{n-1}\)

JZYZOJ 1326 超级教主


题解

首先向考虑小范围的数据。

\(a[i]\)表示前\(i\)个球的总能量,一个前缀和很好理解的

\(f[i]\)表示获得第\(i\)个球后的总能量

呢么很显然我们可以\(DP\)了!!

f[i] = max(f[j] + a[i]-a[j] - 100*i)

呢么这就是一个\(O(n^2)\)的算法。很显然过不了。别给我说\(n^2\)过百万

呢么自然而然就是优化

常见的优化就是压维。呵呵,我不会

好吧,单调性优化

我们发现查找\(j\)是很费时的,我们可不可以用\(O(1)\)的办法把\(j\)找出来呢???

首先我们用一个队列来储存\(j\)的最优值,每次取队头就好了

呢么我们怎么更新队列呢

队头简单如果\(f[j]\)的能量小于当前\(i\)的高度更新掉就好了

队尾比较难理解先看代码

while(f[i] - a[i] > f[q[r]] - a[q[r]]) r--;

\(f[i] - a[i]\) 就是已经获得\(i\)的能量后,损失能量的总和的相反数

很自然\(i\)损失的能量小于\(q[r]\)的能量肯定要舍弃队尾

因为是相反数,所以要变成大于(不等式的性质,不等式两侧同乘一个负数,不等式要变号)


coding

#include <bits/stdc++.h>
using namespace std;


const int N = 2000005;
int n,f[N] = {},a[N] = {};
int cnt,ans = 0,q[N] = {},l = 0,r = 1;


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3)+(x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}


int main()
{
	n = read(); f[0] = read();
	for(register int i = 1;i <= n;i++) a[i] = a[i-1] + read();
	
	for(register int i = 1;i <= n;i++)
	{
		while(f[q[l]] < 100*i && l < r) l++;
		f[i] = f[q[l]] + a[i]-a[q[l]] - 100*i;
		while(f[i] - a[i] > f[q[r]] - a[q[r]]) r--;
		q[++r] = i;
	}
	
	printf("%d\n",f[n]);
	return 0;
}

所以答案为\(m^n - m \times (m-1)^{n-1}\)

快速幂,注意数据范围

#include <bits/stdc++.h>
#define LL long long
using namespace std;


const int p = 100003;
LL n , m ;


inline LL power( LL x , LL y )
{
	register LL ans = 1 ;
	for( ; y ; ) 
	{
		if( y & 1 ) ans = ans * x  % p; 
		x = x * x % p , y >>= 1 ; 
	}
	
	return ans ;
}


int main()
{
	cin >> m >> n;	 
	printf( "%lld\n" ,  ( power( m , n ) + p - m % p  * power( m - 1 , n - 1 ) % p + p ) % p );
	return 0;
} 

【noip2004提高】津津的零花钱题解

题意

津津的零花钱一直都是自己管理。每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。

为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里,到了年末她会加上20% 还给津津。因此津津制定了一个储蓄计划:每个月的月初,在得到妈妈给的零花钱后,如果她预计到这个月的月末手中还会有多于 100 元或恰好 100 元,她就会把整百的钱存在妈妈那里,剩余的钱留在自己手中。

津津发现这个储蓄计划的主要风险是,存在妈妈那里的钱在年末之前不能取出。有可能在某个月的月初,津津手中的钱加上这个月妈妈给的钱,不够这个月的原定预算。如果出现这种情况,津津将不得不在这个月省吃俭用,压缩预算。

现在请你根据2004年1月到12月每个月津津的预算,判断会不会出现这种情况。如果不会,计算到2004年年末,妈妈将津津平常存的钱加上20%还给津津之后,津津 中会有多少钱。

输入格式:
12行数据,每行包含一个小于350的非负整数,分别表示 1 月到 12 月津津的预算。

输出格式:
一个整数。如果储蓄计划实施过程中出现某个月钱不够用的情况,输出 −X , X 表示出现这种情况的第一个月;否则输出到 2004 年年末津津手中会有多少钱。


思路

用循环来写已知循环次数,自然选择for循环,首先先给津津300元,判断是否满足预算,如果满足将整百存在妈妈那里,若不满足输出-X,直接结束程序。结束循环后将津津手里的前加上存在妈妈那里的钱和利息,输出即可。


Coding

#include<bits/stdc++.h>
using namespace std;

int hand_money = 0,mum_money = 0,butget;


int main()
{
	for(int i = 1;i <= 12;i++)
	{
		cin >> butget;
		hand_money += 300;
		if(hand_money < butget)
		{
			cout << '-' << i << endl;
			return 0;
		}
		hand_money -= butget;
		mum_money += hand_money/100;
		hand_money %= 100;
	}
	hand_money = hand_money + mum_money*120;
	cout << hand_money << endl;
	return 0;
}


今天讲的不高兴的津津这道题还有一个加强版的题目

大致思路不变,细节上需要稍作修改P1534 不高兴的津津(升级版)

JZYZOJ 1379 天才约数和

原题来自GDOI2007谁是天才,但找不到链接了

描述 ##

这天周航遇到了靳泽旭。

周航:“我是天才!”

靳泽旭:“你为什么是天才?”

周航:“你随便告诉我一个数字,我立即可以算出它所有约数之和,以及所有约数的倒数和!”

靳泽旭:“换过来,我告诉你一个数的所有约数(包括1和该数本身)的和以及约数的倒数之和,你是天才你应该立即能推出这个数是什么!”

周航被难倒了!

现在,这个难倒了天才的题目就交到你手上了。

输入格式 ##

输入文件包含多组测试数据(最多有\(3000\)组测试数据)。

每组测试数据有三个正整数\(A\),\(B_1\)\(B_2\),\((1\le A,B_1,B_2\le 10^9)\),其中A为C的约数和,而对于\(C\)的所有倒数之和\(B\),为避免精度误差,以分数\(\frac{B_1}{B_2}\)的形式给出。

输入文件以一行0 0 0 结束。

输出格式

对于输入的每一组数据,输出一行。该行的第一个整数N是所有满足条件的不同的C的个数。其后按照从小到大的顺序输出N个数,为所有满足条件的C。相邻两个整数之间用空格隔开,行末不要有空格。

样例输入 ##

18 9 5
1 1 2
1 1 1
0 0 0

样例输出

1 10
0
1 1

思路

首先\(CDCQ\)有个不用数论的方法

首先对于一个数\(a\)如果\(b\)是他的约数,则\(\frac{a}{b}\)也一定是它的约数

所以\(A = a_1+\frac{c}{a_1}+a_2+\frac{c}{a_2}+\cdots+a_n+\frac{c}{a_n}\)

并且\(B = \frac{1}{a_1}+\frac{a_1}{c}+\frac{1}{a_2}+\frac{a_2}{c}+\cdots+\frac{1}{a_n}+\frac{a_n}{c}\)

然后\(B\times C=\frac{c}{a_1}+a_1+\frac{c}{a_2}+a_2+\cdots+\frac{c}{a_n}+a_n=A\)

我们就得到这样一个等式\(B\times C = A \Rightarrow C = \frac{A}{B} = \frac{A\times B_2}{B_1}\)

这样我们就得到了答案\(C\)

但这个答案一定正确么? 不一定吧

比如2 2 2 这样一组数据,我们得到\(C = 2\),但是\(2\)的约数和应该是\(1+2 = 3\)

所以我们应该在验证一下答案时候正确

我们把\(C\)质因数分解成\(C = p_{1}^{a_1}\times p_{2}^{a_2}\times\cdots\times p_{n}^{a_n}\)

\(A = (1+p_{1}^{1}+p_{1}^{2}+\cdots+p_{1}^{a_1})\times(1+p_{2}^{1}+p_{2}^{2}+\cdots+p_{2}^{a_2})\times\cdots\times(1+p_{n}^{1}+p_{n}^{2}+\cdots+p_{n}^{a_n})\)

用这种方式验证一下答案是否正确即可

coding

#include <bits/stdc++.h>
#define LL long long
using namespace std;


const int N = 1100;
LL a,b1,b2;
LL p[N],k[N],cur;


inline LL read()
{
	register LL x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3)+(x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}

inline void divde(LL x)
{
	cur = 0; memset(k,0,sizeof(k));
	
	for(register int i = 2;i * i <= x;i ++)
	{
		if(x%i) continue;
		p[++cur] = i;
		while(x % i == 0) x/= i,k[cur]++;
	}
	if(x == 1) return ;
	p[++cur] = x; k[cur] = 1;
	return ; 
}

inline bool check(LL x)
{
	divde(x);
	register LL sum = 1;
	for(register int i = 1;i <= cur; i ++)
	{
		register LL s1 = 1,s2 = 0;
		k[i] ++;
		while(k[i]--) s2 += s1,s1 *= p[i];
		sum *= s2;
	}
	return sum == a;
}


int main()
{
	a = read(); b1 = read(); b2 = read();
	while(a && b1 && b2)
	{
		if(a * b2 % b1 || b1 < b2)
		{
			puts("0");
			a = read(); b1 = read(); b2 = read();
			continue;
		}
		LL ans = a * b2  / b1;
		if(check(ans)) printf("1 %lld\n",ans);
		else puts("0");
		a = read(); b1 = read(); b2 = read();
	}
	return 0;
}

JZYZOJ P1809 打扫卫生

描述 Description

开学的第一天,jzyz是必须要打扫卫生的。
实验楼一共有n个房间需要打扫,这次给实验楼分到了m位同学,作为打扫卫生的领头人物小x分配任务有一个标准,每个房间至少要有两位同学。

现在小x最关注的是:按照这种标准分配人数,一共有多少种分配方案。

输入格式 Input Format

仅一行,两个整数N,M

输出格式 Output Format

一个整数,方案数。

样例输入 Sample Input

2 7

样例输出 Sample Output

4

注释 Hint

样例2说明:

第一个房间的人数    	第二个房间的人数
2			5
3			4
4			3	
5			2

【数据范围】
对所有数据N<100,M<=500;

题解

这是一道组合数的题

首先我们发现这道题求人数的方案数,所以人是相同的,房间是不同的,这点要注意

对于每个房间最少两个人,我们可以先给每个房间一个人,也就是说在一开始m -= n

剩下的问题就是给每个房间分人,且不可有空房间。

显然用挡板法即可,所以结果就是\(C^{n-1}_{m-1}\)

数据范围小于\(500\)。所以要用到高精度。但是在计算组合数时会用到除法,高精除法非常复杂

所以要先进行约分。因为是组合数,所以结果一定是有理数。约分可以直接约去两个数的\(GCD\)

对于约分后的数,写一个高精乘低精即可,相对简单不少

coding

#include <bits/stdc++.h>
using namespace std;


const int N = 200;
int n,m,bit[N],a[N],b;


inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}

inline void cheng(int x)
{
	for(register int i = 1;i <= bit[0];i ++) bit[i] *= x;
	
	for(register int i = 1;i <= bit[0];i ++)
	{
		if(bit[i] < 10) continue;
		bit[i+1] += bit[i] / 10; bit[i] %= 10;
		if(i == bit[0]) bit[0] ++;
	}
	return ; 
}

inline void output()
{
	for(register int i = bit[0];i >= 1;i --) printf("%d",bit[i]);
	puts("");
	return ;
}


int main()
{
	cin >> n >> m;
	m -= n + 1; n --;
	bit[0] = bit[1] = 1;
	
	for(register int i = 1,j = m;i <= n;i ++,j --) a[i] = j;
	
	for(register int i = 2;i <= n;i ++) // 约分
	{
		register int x = i,y;
		for(register int j = 1;j <= n;j ++)
		{
			if(x > a[j]) y = gcd(x,a[j]);
			else y = gcd(a[j],x);
			x /= y; a[j] /= y;
			if(x == 1) break;
		}
	}
	for(register int i = 1;i <= n;i ++) cheng(a[i]);	

	output();
	return 0;
}

题解 UVA543 【Goldbach's Conjecture】

这道题在#10200. 「一本通 6.2 练习 3」Goldbach's Conjecture也有。

先分析题,验证强哥德巴赫猜想。

首先我们发现要用到素数,而且是多个素数,所以显然要用筛法,因为想巩固一下欧拉筛,所以我直接写的欧拉筛,埃筛应该也可以。

呢么就是暴力枚举小于\(\frac{n}{2}\)的素数,因为两个素数一定是一个大于\(\frac{n}{2}\)、一个小于\(\frac{n}{2}\),并且我们是从小到大枚举所以第一对符合的素数就是结果。

呢么枚举一个素数后另一个数也就确定了,我们只要判断是不是素数就行。因为已经有了素数表,所以只要在素数二分查找即可,但是我懒,直接用lower_bound()就过了。

对于lower_bound()我们只要判断二分出来的数是不是我们要找的数,就可以判断有没有这个数。

#include <bits/stdc++.h>
using namespace std;


const int N = 1e6+5;
int n = 1e6,num,prime[N],cnt;
bool vis[N],flag;


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3) + (x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}


inline void primes()
{
	for(register int i = 2;i <= n;i ++)
	{
		if(!vis[i]) prime[++cnt] = i;
		for(register int j = 1;j <= cnt &&i*prime[j] <= n;j ++)
		{
			vis[i*prime[j]] = i;
			if(i%prime[j] == 0) break;
		}
	}
}

int main()
{
	primes();
	
	while(1)
	{
		num = read();flag = 0;
		if(num == 0) break;
		for(register int i = 1;prime[i] <= num/2 && i <= cnt;i ++)
		{
			register int j = num - prime[i];
			if(j != *lower_bound(prime,prime+cnt,j)) continue;
			flag = 1;
			printf("%d = %d + %d\n",num,prime[i],j);
			break;
		}
		if(flag) continue;
		puts("Goldbach's conjecture is wrong.");
	}
}

「eJOI2019」异或橙子

题目链接

首先先来两个引理

\[a \oplus a = 0\\0\oplus a = a \]

知道这个引理后,我们就可以看下样例

\[a_2 \oplus a_3 \oplus a_4 \oplus (a_2 \oplus a_3) \oplus (a_3 \oplus a_4) \oplus (a_2 \oplus a_3 \oplus a_4)=a_2 \oplus a_2 \oplus a_2 \oplus a_3 \oplus a_3 \oplus a_3\oplus a_3 \oplus a_4 \oplus a_4 \oplus a_4\\=a_2\oplus0 \oplus a_4= 2 \]

也就是说我们可以根据异或的一些性质,来优化一下

如果\(a\)的个数是奇数个其贡献就是\(a\),如果\(a\)的个数是偶数个其贡献就是\(0\)

手推几个数据就能发现

如果\(l,r\)奇偶性不同所有的数都是偶数个,结果自然是\(0\)

如果\(l,r\)奇偶性相同,那么只有和\(l,r\)奇偶性的位置上的数才会有贡献

我们可以开两个树状数组来维护下,一个维护奇数位上的异或前缀和,另一个维护偶数位上的异或前缀和

对于操作1,注意不是把第\(i\)位异或\(j\)是把\(i\)为修改为\(j\),根据异或和的性质我们要先异或\(a[i]\)在异或\(j\),所以可以异或$a[i]\oplus j $

对于操作2首先特判奇偶性不同的,对于奇偶性相同的,我们在对应的树状数组里求出\(r,l-1\)的异或前缀和,在异或一下即可

#include <bits/stdc++.h>
#define lowbit( x ) ( x & - x )
using namespace std;


const int N = 2e5 + 10;
int n , m , bit[2][N] , a[N];


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' )
	{
		x = ( x << 3 ) + ( x << 1 ) + ch - '0';
		ch = getchar();
	}
	return x;
}

inline void add( int val , int pos , int k )
{
	for( register int i  = pos ; i <= n ; bit[k][i] ^= val , i += lowbit( i ) );
}

inline int find( int pos , int k )
{
	register int res = 0;
	for( register int i = pos ; i >= 1 ; res ^= bit[k][i] , i -= lowbit( i ) );
	return res;
}

int main()
{
	n = read() , m = read();
	for( register int i = 1  ; i <= n ; a[i] = read() , add( a[i] , i , i & 1 ) ,  i ++ );
	for( register int i = 1 , op , l , r ; i <= m ; i ++ )
	{
		op = read() , l = read() , r = read();
		if( op == 1 ) add( a[l] ^ r , l , l & 1 ) , a[l] = r ;
		else printf( "%d\n" , ( ( l + r ) & 1 ) ? 0 : ( find( r , r & 1 ) ^ find( l - 1 , r & 1 ) ) );
	}
	return 0;
}

逃亡的准备(大数据版)

题目来自JZYZOJ。找了很多地方都没有找到题,还是一道非常优秀的二进制优化多重背包的问题


描述 Description

在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡,现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品,现在给出该种物品的数量、体积、价值的数值,希望你能够算出怎样能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。


输入格式 Input Format

\(1\)行有\(2\)个整数,物品种数\(n\)和背包装载体积\(v\)

\(2\)行到\(n+1\)行每行\(3\)个整数,为第\(i\)种物品的数量\(m\)、体积\(w\)、价值\(s\)


输出格式 Output Format

仅包含一个整数,即为能拿到的最大的物品价值总和。


样例输入 Sample Input

2 10                            
3 4 3
2 2 5


样例输出 Sample Output

13

注释 Hint

对于$ 100% $的数据

$1 \le v \le 5000 $

$1 \le n \le 5000 $

$1 \le m \le 5000 $

$1 \le w \le 5000 $

$1 \le s \le 5000 $


如果数据范围比较小的话可以直接多重背包但这道题数据很大自然要用到二进制优化

对于多重背包,枚举每一种物品的每一种数量自然是多次重复的运算

我们已知\(1\) , \(2\) , \(4\) , \(8\) , \(16\) , \(32……\),\(2^n\)可以组成\(1\)\(2^{n+1}-1\)之内的任意一个数字

所以我们对于给定\(n\),把他分解成\(2^x\)的数,剩下的数进行补齐,即可转换成01背包做

转换代码

for(register int i = 1;i <= n;i++)
{
	for(register int j = 1;j <= m[i];j <<= 1)
	{
		tw[++tot] = j*w[i];
		tv[tot] = j*v[i];
		m[i] -= j;
	}
	if(m[i])//补齐剩下的数
	{
		tw[++tot] = m[i]*w[i];
		tv[tot] = m[i]*v[i];
	}
}

另外新的数组\(tw,tv\)要开成原数组的\(max(x)\)


coding##

#include <bits/stdc++.h>
using namespace std;

const int N = 5005;
int n,V,tot = 0,v[N] = {},m[N] = {},w[N] = {}, tw[N*20] = {},tv[N*20] = {},f[N] = {};



inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3) + (x<<1) + ch-'0';
		ch = getchar();
	}
	return x;
}

inline void optimize()
{
	for(register int i = 1;i <= n;i++)
	{
		for(register int j = 1;j <= m[i];j <<= 1)
		{
			tw[++tot] = j*w[i];
			tv[tot] = j*v[i];
			m[i] -= j;
		}
		if(m[i])
		{
			tw[++tot] = m[i]*w[i];
			tv[tot] = m[i]*v[i];
		}
	}
}


int main()
{
	n = read(); V = read();
	for(register int i = 1;i <= n;i++) m[i] = read(), w[i] = read(), v[i] = read();
	
	optimize();
	for(register int i = 1;i <= tot;i++)
	{
		for(register int j = V;j >= tw[i];j--) f[j] = max(f[j],f[j-tw[i]]+tv[i]);
	}
	printf("%d\n",f[V]);
	return 0;
}

查找最接近的元素

计蒜客 T1156 查找最接近的元素

这是到挺有意思的题,做为饭后消食还是不错的选择

首先题目说不降序列也就是说序列是有序的呢就好办了,直接二分

分析易知,和一个数\(x\)最接近的数只有三种情况

  • 大于\(x\)的数中最小的
  • 小于\(x\)的数中最大的
  • \(x\)本身

首先我这么懒的人肯定是不想手写二分的所有目光自然就落了lower_bound()upper_bound()

然后upper_bound()得到的结果是大于\(x\)的数中最小的,所以upper_bound()更合适

呢么令\(t\)是二分出来的数的下标,呢么\(t-1\),要么就是\(x\),要么就是小于\(x\)的数中最大的,比较一下就好

#include <bits/stdc++.h>
using namespace std;


const int N = 100005;
int n , m , a[N] , t , s ;


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 1 ) + ( x << 3 ) + ch - '0';
        ch = getchar();
    }
    return x;
}


int main()
{
    n = read();
    for( register int i = 1 ; i <= n ; i ++ ) a[i] = read();
    m = read();
    for( register int i = 1 ; i <= m ; i ++ )
    {
        s = read();
        if( s < a[1] )
        {
            printf( "%d\n" , a[1] );
            continue;
        }
        if( s > a[n] )
        {
            printf( "%d\n" , a[n] );
            continue;
        }
        t = upper_bound( a + 1 , a + 1 + n , s ) - a;
        if( a[t] - s  < s - a[t - 1] ) printf( "%d\n" , a[t] );
        else printf( "%d\n" , a[ t - 1 ] );
    }
    return 0;
}

JZYZOJ P1695 极品飞车 题解

描述 Description

\(FC\)星有许多城市,城市之间通过一种奇怪的高速公路\(SARS\)(\(Super\quad Air\quad Roam\quad Structure\)---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的\(Flycar\)限制了固定的\(Speed\),同时\(FC\)星人对 \(Flycar\)的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为\(SARS\)的限速要求,\(flycar\)必须瞬间提速/降速,痛苦呀 ),但\(FC\)星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(\(SARS\)是双向的)。

输入格式 Input Format

第一行有\(2\)个正整数\(n(1<n<201)\)\(m(m<1001)\),表示有\(N\)个城市和\(M\)\(SARS\)

第二行是三个正整数\(StartCity\),\(EndCity\),\(speed\),表示从表面上看\(StartCity\)\(EndCity\),限速为\(speed\)\(SARS\)。然后是一个正整数\(Q(Q<11)\),表示寻路的个数。
接下来\(Q\)行每行有\(2\)个正整数\(Start\),\(End\), 表示寻路的起终点。

输出格式 Output Format

每个寻路要求打印一行,仅输出一个非负整数表示最佳路线的舒适度最高速与最低速的差。

如果无解,输出\(32475...\)(不要问为什么..我就是被这么坑的)

样例输入 Sample Input

4 4
1 2 2
2 3 4
1 4 1
3 4 2
2
1 3
1 2

样例输出 Sample Output

1
0

思路

这道题应该是由HDOJ 1598题改编来的,呢道有多组数据这道没有

首先说一下用什么算法枚举+贪心+Kruskal

先分析一下题目

给一个无向图,\(K\)次询问每次询问两个点之间边权差值的最小值

假设我已经知道了选择某些边,我只要对边按照边权排序,用最后一条边的权值减去第一条的权值即可

所以我们要先对所有的边进行排序,这是第一步

其次我怎么知道选哪一些呢

通过观察上面的过程我就们发现对结果有影响的只有最大边和最小边,所有我们采用枚举的方法即可,枚举最小边和最大边

既然枚举边,我们就要判断这组边是否符合条件对吧

显然只要是满足大于最小边小于最大边的边我都可以使用,所以我们对最小边到最大边之间所有的边跑一边\(Kruskal\)即可判断

当然我们还可以进行一点优化

我们枚举按照已经排好的顺序枚举,先枚举最小边,再枚举最大边,,所以一旦某一次枚举的最大边满足条件,呢么这组边就是当前最小边的最优解,不必再枚举后面的边

coding

#include <bits/stdc++.h>
using namespace std;


const int N = 205,M = 1005,INF = 0x7f7f7f7f;
int n,m,father[N] = {},T;

struct edge
{
	int u,v,w;
	friend bool operator < (edge a,edge b)
	{
		return a.w < b.w;
	}
}e[M];


inline int read()
{
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9')
	{
		x = (x<<3)+(x<<1) + ch -'0';
		ch = getchar();
	}
	return x;
}

inline int getfather(int x)
{
	if(x == father[x]) return x;
	return father[x] = getfather(father[x]);
}

inline void Kruskal()
{
	register int star = read(), end = read(),result = INF;
	
	for(register int i = 1;i <= m;i ++)
	{
		for(register int j = 1;j <= n;j ++) father[j] = j;
		
		for(register int j = i;j <= m;j ++)
		{
			register int fu = getfather(e[j].u), fv = getfather(e[j].v);
			if(fu != fv) father[fu] = fv;
			if(getfather(star) == getfather(end))
			{
				result = min(result,e[j].w - e[i].w);
				break;
			}
		}
		if(result == 0) break;
	}
	if(result == INF) puts("32475");
	else printf("%d\n",result);
	return ;	
}


int main()
{
	n = read(); m = read();
	for(register int i = 1;i <= m;i ++) e[i].u = read(), e[i].v = read(), e[i].w = read();
	sort(e+1,e+1+m);
	
	T = read();
	while(T --) Kruskal();
	
	return 0;
}

JZYZOJ 1629 找分子

题目描述

给定\(n(2≤n≤10^9)\)值,要求\(x\)\(y\)均为正整数,且\(x<y\),并且满足\(\frac{1}{x}+\frac{1}{y}=\frac{1}{n}\).编程统计有多少对这样的\(x\)\(y\)

输入格式

一个整数n

输出格式

一个整数,表示相应的方法数是多少。

样例输入

6

样例输出

4

思路

先把原方程化简
\(\frac{1}{x}+\frac{1}{y}=\frac{1}{n} \Rightarrow \frac{xy}{x+y} = \frac{1}{n} \Rightarrow xy = nx + ny \Rightarrow xy-nx-ny = 0 \Rightarrow xy - nx -ny + n^2 = n^2 \Rightarrow n^2 = (n-x)(n-y)\)

所以原方程等价于\(n^2 = (n-x)(n-y)\)

\(a = n-x , b = n-y\)\(a < b\)

\(n^2=ab\)

不难发现\(a\)\(b\)\(n^2\)的一组因子

如果\(n^2\)\(k\)个因子,就有\(\frac{k}{2}\)组解,对应了\(\frac{k}{2}\)\(x\)\(y\)

我们把\(n\)进行质因数分解得$n=a_{1}^{p_{1}}\times a_{2}^{p_{2}}\times a_{3}^{p_{3}}\times \dots a_{m}^{p_{m}} $

\(n\)\(\Pi_{i=1}^{m}(p_{i}+1)\)个因子

同样把\(n^2\)进行质因数分解得$n{2}=a_{1}{2p_{1}}\times a_{2}^{2p_{2}}\times a_{3}^{2p_{3}}\times \dots a_{m}^{2p_{m}} $

\(n^2\)\(\Pi_{i=1}^{m}(2p_{i}+1)\)个因子

所以\(result =\frac{k}{2}=\frac{1}{2}\Pi_{i=1}^{m}(2p_{i}+1)\)

coding

#include <bits/stdc++.h>
using namespace std;


int n,result = 1;


int main()
{
	cin >> n;
	for(register int i = 2;i * i <= n;i ++)
	{
		if(n % i) continue;
		register int cnt = 0;
		while(n % i == 0) cnt ++, n /= i;
		result *= cnt * 2  + 1;
	}
	if(n > 1) result *= 3;
	result >>= 1;
	cout << result << endl;
	return 0;
}

posted @ 2019-10-30 00:51  PHarr  阅读(283)  评论(0编辑  收藏  举报