hihoCoder-[Offer收割]编程练习赛99

A:纸牌游戏

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述:
小Hi有N张纸牌。每张纸牌都有三种属性,每个属性的值可能是0、1、2三种之一。 现在小Hi想从N张牌中选取3张凑成一套。3张牌可以凑成一套的条件是: 对于每一种属性,这三张牌的值或者两两不同,或者全部相同。 假设我们用三元组(A, B, C)来表示一张牌,那么(0, 1, 2)(0, 2, 0)(0, 0, 1)可以凑成一套。因为第一种属性都是0;第二种属性分别是1、2、0各不相同;第三种属性分别时2、0、1各不相同。
小Hi想知道这N张牌一共有多少种凑成一套的选法。
输入
第一行包含一个整数N。
以下N行每行三个整数A, B, C代表一张牌的三个属性。
对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= A, B, C <= 2
输出
一个整数代表答案

样例输入:
5
0 0 0
0 0 0
1 1 1
2 2 2
2 2 2

样例输出:
4

解释 :
因为只有3个属性每个属性有3种值,所以可以把它看成一个3进制数,这样每张牌变成0~26的数,利用哈希来统计一下每个数有多少个,最后再三重循环暴力统计一下就OK

#include<iostream>
#include<cstdio>
#define N 100005
#define mod 1000000009
using namespace std;
long long H[N]={0};
long long ret=0;
bool ok(int a,int b,int c){
	int sum=0;
	if((a%3==b%3&&c%3==a%3)||!(a%3==b%3||c%3==a%3||b%3==c%3)) sum++;
	a/=3;b/=3;c/=3;
	if((a%3==b%3&&c%3==a%3)||!(a%3==b%3||c%3==a%3||b%3==c%3)) sum++;
	a/=3;b/=3;c/=3;
	if((a%3==b%3&&c%3==a%3)||!(a%3==b%3||c%3==a%3||b%3==c%3)) sum++;
	a/=3;b/=3;c/=3;
	if(sum>=3) return 1;
	else return 0;
}
int main(){
	int n=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int a=0,b=0,c=0;scanf("%d%d%d",&a,&b,&c);
		H[a*9+b*3+c]++; 
	}
	for(int i=0;i<27;i++){
		for(int j=i+1;j<27;j++){
			for(int k=j+1;k<27;k++){
				if(ok(i,j,k)) ret+=H[i]*H[j]*H[k];
			}
		}
		if(H[i]>=3) ret+=(H[i]-2)*(H[i]-1)*(H[i])/6;
	}
	for(int i=0;i<27;i++){
		for(int j=i+1;j<27;j++){
			if(H[i]>=2&&ok(i,i,j)){
				ret+=(H[i]-1)*H[i]/2*H[j];
			}
			if(H[j]>=2&&ok(i,j,j)){
				ret+=(H[j]-1)*H[j]/2*H[i];
			}
		}
	}
	printf("%lld\n",ret);
	return 0;
} 

B:多项式系数

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述:
给定多项式(ax + by)k 的三个参数a、b和k。请你计算该多项式展开后,xnym这一项的系数。(n + m = k)
由于答案可能非常大,你只需输出结果模1000000007的余数。
输入
一行包含5个整数a、b、k、n和m。
1 <= a, b, k <= 1000000 n + m == k。
输出
一个整数代表答案。

样例输入
2 3 3 1 2

样例输出
54

解释:通过二项式定理展开可以得到答案就是 ( k n ) \binom k n (nk) a n a^{n} an b m b^{m} bm,a和b项可以通过快速幂计算,组合数还需要乘它的模数逆元

#include<iostream>
#include<cstdio>
using namespace std;
long long mod=1000000007LL;
long long ok(long long a,long long b,long long p){
	long long ret=1;
	while(b){
		if(b&1) ret=ret*a%p;
		b>>=1;a=a*a%p;
	}
	return ret;
}
int main(){
	long long a=0,b=0,k=0,n=0,m=0;
	cin>>a>>b>>k>>n>>m;
	long long ans=0;
	ans=ok(a,n,mod)*ok(b,m,mod)%mod;
	long long temp=1;
	for(long long i=1;i<=n;i++){
		temp=temp*(k-n+i)%mod;
		temp=temp*ok(i,mod-2,mod)%mod;
	}
	ans=ans*temp%mod;
	cout<<ans<<endl;
	return 0;
}

D:有向图的关键点

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述
给定一个包含N个节点和M条边的有向图,其中节点的编号是1~N。如果从一个点V出发,可以到达所有其他的节点,我们就称V是关键点。
请你计算图中一共有多少个关键点。
输入
第一行包含两个整数N和M。
以下M行每行包含两个整数u和v,代表一条从u到v的有向边。
1 <= N <= 100000 0 <= M <= 200000 1 <= u, v <= N
输出
一个整数代表答案

样例输入
4 4
1 2
2 3
1 3
3 4

样例输出
1

解释:首先如果是关键点V,那么从关键点V开始DFS遍历,一定可以遍历完整张图,那么我们从1~n开始遍历,那最后一个没有遍历的一定是关键点(如果它存在的话),为了验证它的存在,重新在这个点上进行一次遍历,如果不能遍历全部的点,则就不存在关键点,否则存在。其他关键点U必然也能到达V,那么U和V之间就是一个相互可达的。其实就是一个强联通分量。这样我们跑一次强联通分量的模板,那么V在的强联通分量的个数就是所有关键点的个数。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100001
#define MAX_N 100001 
#define MAX_M 200001 
using namespace std;
int head[N]={0};
int next1[N]={0};
int V[N]={0};
int tot=0;
int root=0;
//vector <int> vec[MAX_N];
//bool ok=1;
int dfn[MAX_N], low[MAX_N], stap[MAX_N], belong[MAX_N];
int n, m, bcnt, dindex = 0, stop = 0;
int rd[MAX_N], rd0cnt;
bool instack[MAX_N];
 
int x[MAX_M], y[MAX_M];
 
void tarjan(int v) {
	int u;//l = vec[v].size();
	dfn[v] = low[v] = ++dindex;
	instack[v] = true;
	stap[++stop] = v;
	for (int i = head[v]; i; i=next1[i]){
		u = V[i];
		if (!dfn[u]) {
			tarjan(u);
			if (low[u] < low[v])
				low[v] = low[u];
		}
		else if (instack[u] && dfn[u] < low[v])
			low[v] = dfn[u];
	}
	if (dfn[v] == low[v]) {
		bcnt++;
		do {
			u = stap[stop--];
			instack[u] = false;
			belong[u] = bcnt;
		}
		while (u != v);
	}
}
void add(int x,int y){
	tot++;
	next1[tot]=head[x];
	V[tot]=y;head[x]=tot;
}
bool vis[N]={0};
bool ok=1;
void dfs(int rt){
	vis[rt]=1;
	for(int i=head[rt];i;i=next1[i]){
		int to=V[i];
		if(vis[to]) continue;
		dfs(to);
	}
}
int num[N]={0};
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		int a=0,b=0;scanf("%d%d",&a,&b);
		add(a,b);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			dfs(i);
			root=i;
		}
	}
	memset(vis,0,sizeof(vis));dfs(root);
	for(int i=1;i<=n;i++){
		if(!vis[i]) ok=0;
	}
	if(!ok){
		printf("0\n");
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i);
	}
	for(int i=1;i<=n;i++){
		num[belong[i]]++;
	}
	cout<<num[belong[root]]<<endl;
	return 0;
} 

C:字符统计

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述
对于一个字符串S,我们定义fk(S)的值是S中出现次数不超过k次的字符个数。
例如S="abbccc"中A和B出现次数不超过2,所以f2(“abbccc”) = 2;同理f3(“abbccc”) = 3, f1(“hiho”) = 2。
现在给定一个只包含小写字母的字符串S和一个整数k。小Hi想知道对于S的所有子串T = S[i … j] (1 <= i <= j <= |S|),fk(T)的和是多少。
输入
第一行包含一个整数k。
第二行包含一个由小写字母组成的字符串S。
对于50%的数据,1 <= |S| <= 10000
对于100%的数据,1 <= |S| <= 100000 1 <= k <= 1000
输出
一个整数代表答案

样例输入
1
hiho

样例输出
16

解释:
因为总共26个字母,所以可以枚举每个字母,然后找<=k的字母的子串,pos[i][j]:为字母i,出现第j次的位置是啥
最后计数一下就OK

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
using namespace std;
int pos[26][N]={0};
char str[N];
long long ret=0;
int k=0;
int main(){
    scanf("%d",&k);
    scanf("%s",str+1);
    int len=strlen(str+1);
    for(int i=0;i<26;i++){
        int now=0;
        for(int j=1;j<=len;j++){
            if(str[j]-'a'==i){
                now++;
                pos[i][now]=j;
            }
            if(!now) continue;
            if(now<=k) ret+=pos[i][now];
            else{
                ret+=pos[i][now]-pos[i][now-k];
            }

        }
    }
    cout<<ret<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值