给出一个n。问存在多少个m进制( m < n )使得在m进制下末尾为0。就是求n的因子个数。
打表10^6以内的素数,然后枚举素数
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000010;
int T,Case=1,cnt=0,vis[maxn],prime[maxn];
long long n,sum;
void isprime() //素数表
{
memset(vis,0,sizeof(vis));
for(int i=2;iif(!vis[i])
{
for(int j=i+i;j1;
prime[cnt++]=i;
}
}
}
int main()
{
scanf("%d",&T);
isprime();
while(T--)
{
sum=1;
scanf("%lld",&n);
for(int i=0;i//欧拉函数,质因子分解法
{
if(n%prime[i]==0)
{
int tmp=1;
while(n%prime[i]==0)
{
tmp++;
n/=prime[i];
}
sum*=tmp;
}
}
if(n>1)
sum*=2;
printf("Case %d: %lld\n",Case++,sum-1);
}
}
有2^n支队伍进行比赛,每行给出这支队伍打败各支队伍的几率,求出那支队伍获胜几率最大
方程就是每一只队伍,在每一轮与可能遇到的队伍,并战胜的概率乘起来就可以了
这题不好处理的就是分组问题。一开始从1~top。WA了好多次,最后仔细动手算了下发现,这种方法不能保证在右移之后,满足所的到的性质,所以要从0~top-1来存放
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1<<8;
double pec[maxn][maxn],dp[10][maxn];
int main()
{
int n;
//freopen("in.txt","r",stdin);
while( scanf("%d",&n) != EOF )
{
if( n == -1 )
break;
int top = 1<for( int i = 0; i < top; i++ )
for( int j = 0; j < top; j++ )
scanf("%lf",&pec[i][j]);
memset(dp,0,sizeof(dp));
for( int i = 0; i < top; i++ )
dp[0][i] = 1;
for( int i = 1; i <= n; i++ )
{
for( int j = 0; j < top; j++ )
for( int k = 0; k < top; k++ )
{
int p = j >> (i-1);
int q = k >> (i-1);
if( q % 2 )
{
q--;
if( p == q )
dp[i][j] += dp[i-1][j]*dp[i-1][k]*pec[j][k];
}
else
{
q++;
if( p == q )
dp[i][j] += dp[i-1][j]*dp[i-1][k]*pec[j][k];
}
}
}
int index = 0;
for( int i = 0; i < top; i++ )
if( dp[n][index] < dp[n][i] )
index = i;
printf("%d\n",index+1);
}
return 0;
}
求S串中的第N个数字。
找规律,先把前面的串都去掉,然后模9就是答案。
#include
long long k,t,i,j,ans;
int main(){
scanf("%lld",&k);
while(k--){
scanf("%lld",&t);
i=1;
while(1){
t-=i;
if(t<=0){
t+=i;
break;
}
i++;
}
if(t%9==0)
ans=9;
else
ans=t%9;
printf("%lld\n",ans);
}
return 0;
}
有一个n个点的无向图,给出m条边的边权,给出q次询问,每次给出一个值,求用到所有边权不大于这个值的边的情况下,能够互相到达的点对的个数(自己到自己不算)
res += ( ( num[u] + num[v] ) * ( num[u]+num[v] – 1) – num[u]( num[u]-1 ) – num[v](num[v]-1) );
表示从两个连通块中任意取两个点。因为res是累加的,所个单个连通块中的情况已经计算过了,不扣除会重复计算
#include
#include
#include
#include
const int maxn = 20005;
const int maxm = 100005;
using namespace std;
struct Edge{
int u,v,w;
bool operator < ( const Edge &a )const {
return w < a.w;
}
}edge[maxm];
struct Node{
int id,x;
bool operator < ( const Node &a )const{
return x < a.x;
}
}node[5005];
int f[maxn];
int ans[maxn],num[maxn];
void init( int n ){
for( int i = 1; i <= n; i++ ){
num[i] = 1;
f[i] = i;
}
}
int find( int x ){
if( f[x] != x ){
return f[x] = find( f[x] );
}
else{
return x;
}
}
void join( int x, int y ){
f[y] = x;
num[x] += num[y];
}
int main(){
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while( T-- ){
int n,m,q;
scanf("%d %d %d",&n,&m,&q);
init( n );
for( int i = 0; i < m; i++ ){
scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+m);
for( int i = 0; i < q; i++ ){
scanf("%d",&node[i].x);
node[i].id = i;
}
sort( node, node+q );
int res = 0;
int j = 0;
for( int i = 0; i < q; i++ ){
while( j < m && edge[j].w <= node[i].x ){
int u = find( edge[j].u );
int v = find( edge[j].v );
j++;
if( u == v ){
continue;
}
//printf("res:%d num[u]:%d num[v]:%d\n",res,num[u],num[v]);
res += ( ( num[u] + num[v] ) * ( num[u]+num[v] - 1) - num[u]*( num[u]-1 ) - num[v]*(num[v]-1) );
join( u, v );
}
ans[node[i].id] = res;
}
for( int i = 0; i < q; i++ ){
printf("%d\n",ans[i]);
}
}
return 0;
}
求满足题目条件的序列个数(坑点是:数字是从1到N并且没重复的)
b,c数组第一个数肯定要相等,两个数组相邻的数不可能同时不相等,因为数从1到N,所以c[i]-b[i]>=i。当两个数组相邻数字都相等时就能计算个数。
#include
#define MAXN 1000005
int n,b[MAXN],c[MAXN];
long long ans;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i"%d",&b[i]);
for(int i=0;i"%d",&c[i]);
if(b[0]!=c[0]){
printf("%d\n",0);
continue;
}
ans=1;
for(int i=1;iif(b[i-1]!=b[i]&&c[i-1]!=c[i]){ans=0;break;}
if(c[i]-b[i]0;break;}
if(b[i-1]==b[i]&&c[i-1]==c[i])
ans=ans*(c[i]-b[i]-i+1)%998244353;
}
printf("%lld\n",ans);
}
return 0;
}
给出一个序列ci,然后自己构造一个1-n的全排列,对于某个全排列f(x) = sigma{ ci * (h[i] > h[i – 1] && h[i] > h[i + 1] ? 1 : 0) } ,求f(x)的期望。(h[i] > h[i – 1] && h[i] > h[i + 1] ? 1 : 0) 表示当我存在一个h[i]大于左右两边的值时,就取相应i值的ci值,否则不取。sigma是求和操作。
对于头和尾大于旁边的概率一定是0.5(用头部举例,头部和它右边的数一共有2中排列,头部大于它右边的排列只有1种,那么概率就是1/2),因此头和尾贡献是(a[1] + a[n]) * 0.5,其他部分:任意取三个数,只有一种情况满足h[i] > h[i + 1] && h[i] > h[i – 1],因此他们部分大于旁边的概率是1/3(中间的数和左右两边的数共有3!=3*2*1=6中排列,其中中间值大于左右两边值的排列有2种,所以概率是2/6=1/3),因此(sum – a[1] – a[n]) / 3是其他部分的贡献。对于n==1的情况要特判。
#include
#include
#include
#include
using namespace std;
int n;
int main()
{
while(~scanf("%d",&n))
{
int a;
double sum1=0,sum2=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
if(i==1||i==n) sum1+=a;
else sum2+=a;
}
if(n==1)
{
printf("%.6f\n",a*1.0);
}
else
{
double ans=sum1/2+sum2/3;
printf("%.6f\n",ans);
}
}
return 0;
}
给你n个数的序列,和一个询问次数q,对于每次q给出l,r,问你在[l,r]这个范围内序列中最大的数。
线段树模版题,本题数据很水暴力也可以过。
线段树版本:
#include
#include
#include
#include
#define lson rt << 1
#define rson rt << 1 | 1
const int maxn = 1e3 + 10;
using namespace std;
struct Node{
int l, r;
int sum;
int mid(){
return ( l + r ) >> 1;
}
}tree[maxn<<2];
void PushUp( int rt ){
tree[rt].sum = max( tree[lson].sum, tree[rson].sum );
}
void build( int l, int r , int rt ){
tree[rt].l = l;
tree[rt].r = r;
if( l == r ){
scanf("%d",&tree[rt].sum);
return;
}
int mid = ( l + r ) >> 1;
build( l, mid, lson );
build( mid+1, r, rson);
PushUp( rt );
}
int query( int l, int r, int rt ){
if( l <= tree[rt].l && tree[rt].r <= r ){
return tree[rt].sum;
}
int mid = tree[rt].mid();
if( r <= mid ){
return query( l, r, lson );
}
else if( l > mid ){
return query( l, r, rson );
}
else{
int a = query( l , mid, lson );
int b = query( mid+1, r, rson );
return max(a,b);
}
}
int main(){
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while( T-- ){
int n;
scanf("%d",&n);
build( 1, n, 1 );
int Q;
scanf("%d",&Q);
while( Q-- ){
int l,r;
scanf("%d %d",&l,&r);
printf("%d\n",query(l,r,1));
}
}
return 0;
}
暴力版本:
#include
#include
#include
#include
using namespace std;
const int maxn=1000+5;
int arr[maxn],t,q,n;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int l,r;
scanf("%d %d",&l,&r);
int max=-1;
for(int j=l;j<=r;j++)
{
if(arr[j]>max)
{
max=arr[j];
}
}
printf("%d\n",max);
}
}
return 0;
}
求最多能划分成几个前缀和都大于等于0的区间
从后往前贪心,如果是负数就继续向前,否则答案+1
#include
#define MAXN 1000005
long long n,ans,a[MAXN];
int main(){
while(~scanf("%lld",&n)){
for(long long i=1;i<=n;i++)
scanf("%lld",&a[i]);
ans=0;
for(long long i=n;i>=1;i--){
if(a[i]>=0)
ans++;
else
a[i-1]+=a[i];
}
printf("%lld\n",ans);
}
return 0;
}
求逆序对个数
离散化+线段树也可以做,二分利用归并排序,中间加个计数ans+=(m-i+1)即可。
线段树版本:
#include
#include
#include
#include
using namespace std;
typedef __int64 INT;
const int maxn = 65537+5;
struct node
{
int data,cixu,dd;
}que[maxn];
struct Node
{
INT data;
int l,r;
}tree[2*maxn];
bool cmp1(node a,node b)
{
return a.data <= b.data;
}
bool cmp2(node a,node b)
{
return a.cixu < b.cixu;
}
void maketree(int p)
{
if(tree[p].l == tree[p].r)
return ;
int mid = (tree[p].l + tree[p].r) / 2;
tree[2*p].data = 0;
tree[2*p].l = tree[p].l;
tree[2*p].r = mid;
maketree(2*p);
tree[2*p+1].data = 0;
tree[2*p+1].l = mid + 1;
tree[2*p+1].r = tree[p].r;
maketree(2*p+1);
}
INT find(int p,int l,int r)
{
if(l > r)
return 0;
if(tree[p].l == l && tree[p].r == r)
return tree[p].data;
int mid = (tree[p].l + tree[p].r) / 2;
if(r <= mid)
return find(2*p,l,r);
else if(l <= mid && r > mid)
return find(2*p,l,mid) + find(2*p+1,mid + 1,r);
else return find(2*p+1,l,r);
}
void push(int p,int d)
{
tree[p].data++;
if(tree[p].l == tree[p].r)
return ;
int mid = (tree[p].l + tree[p].r) / 2;
if(d <= mid)
push(2*p,d);
else push(2*p+1,d);
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
scanf("%d",&que[i].data);
que[i].cixu = i;
}
sort(que+1,que+n+1,cmp1);
int f = -1;
que[0].data = 0x7fffffff; //只要一个跟所有输入的数都不同的就行了
for(int i = 1;i <= n;++i)
{
if(que[i].data != que[i-1].data)
f++;
que[i].dd = f;
}
tree[1].data = 0;
tree[1].l = 0;
tree[1].r = f;
maketree(1);
sort(que+1,que+n+1,cmp2);
INT tot = 0;
for(int i = 1;i <= n;++i)
{
tot += find(1,que[i].dd + 1,f);
push(1,que[i].dd);
}
printf("%I64d\n",tot);
return 0;
}
二分+归并:
#include
#define MAXN 100005
int a[MAXN],tmp[MAXN];
long long ans;
void Merge(int l,int m,int r){
int i=l,j=m+1,k=l;
while(i<=m&&j<=r){
if(a[i]>a[j]){
tmp[k++]=a[j++];
ans+=(m-i+1);
}
else
tmp[k++]=a[i++];
}
while(i<=m)
tmp[k++]=a[i++];
while(j<=r)
tmp[k++]=a[j++];
for(int i=l;i<=r;i++)
a[i]=tmp[i];
}
void Merge_sort(int l,int r)
{
if(lint m=(l+r)/2;
Merge_sort(l,m);
Merge_sort(m+1,r);
Merge(l,m,r);
}
}
int main()
{
int n;
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ans=0;
Merge_sort(1,n);
printf("%lld\n",ans);
}
return 0;
}
4*4数独问题
对于每一次dfs判断行和列以及所在2*2小宫格内是否存在一个数和当前放的数相等,相等就回溯,否则可行。
#include
#include
#include
#include
#include
using namespace std;
const int maxn=20;
char map[maxn][maxn];
int t,num[maxn][maxn];
bool flag;
bool check(int x, int y)
{
for(int i=0;i<4;i++)//判断列上有没有相等的数字
{
if(i!=x&&num[i][y]==num[x][y])
return false;
}
for(int i=0;i<4;i++)//判断行上有没有相等的数字
{
if(i!=y&&num[x][i]==num[x][y])
return false;
}
int nx=(x/2)*2;//所在小宫格的最左上顶点的x坐标
int ny=(y/2)*2;//所在小宫格的最坐上顶点的y坐标
for(int i=nx;i2;i++)//判断所在2*2小宫格内是否有相等的数
for(int j=ny;j2;j++)
{
if(i==x&&j==y)
continue;
if(num[x][y]==num[i][j])
return false;
}
return true; //如果全部不符合表示可以放
}
void dfs(int x,int y)
{
if(flag||x>3)//全部放完,并且找到解
{
flag=true;
return ;
}
if(num[x][y])//如果当前位置已经放了数字,就dfs下一个位置
{
if(y==3)
dfs(x+1,0);
else
dfs(x,y+1);
if(flag)
return;
}
else//如果当前位置没有放数字,则从1~4选一个数字放判断是否可行。
{
for(int i=1;i<=4;i++)
{
num[x][y]=i;
if(check(x,y))//可行,则dfs下一个位置
{
if(y==3)
dfs(x+1,0);
else
dfs(x,y+1);
}
if(flag)
return;
num[x][y]=0;//不可行,回滚到初始状态
}
}
}
int main()
{
scanf("%d",&t);
int cas=1;
while(t--)
{
flag=false;
for(int i=0;i<4;i++)
{
cin>>map[i];
for(int j=0;j<4;j++)
{
if(map[i][j]=='*') num[i][j]=0;
else num[i][j]=map[i][j]-'0';
}
}
dfs(0,0);
printf("Case #%d:\n",cas++);
for (int i=0;i<4;i++)
{
for(int j=0;j<3;j++)
{
cout<cout<3];
cout<return 0;
}
扔棍子,要求扔出去的棍子长度和地上已有的棍子长度不能重复并且不能喝地上的任意两个棍子构成三角形,求可行的方案数。
利用三角形性质,把两边之和,两边之差作为区间,扫描一边区间即可
#include
#include
#include
using namespace std;
long long n,l,r,a[100005],ans,maxt;
int t;
pair<long long,long long> p[100005];
int main(){
scanf("%d",&t);
while(t--){
ans=0;
scanf("%lld%lld%lld",&n,&l,&r);
for(int i=0;iscanf("%lld",&a[i]);
sort(a,a+n);
for(int i=0;i1;i++){
p[i].first=a[i+1]-a[i];
p[i].second=a[i+1]+a[i];
}
sort(p,p+n-1);
maxt=l;
for(int i=0;i1&&p[i].first<=r;i++){
if(p[i].first>=maxt)
ans+=(p[i].first-maxt+1);
maxt=max(maxt,p[i].second);
}
if(r-maxt+1>0)
ans+=r-maxt+1;
printf("%lld\n",ans);
}
return 0;
}