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;
}