2017ACM/ICPC广西邀请赛

本文探讨了无向图中特定结构的计数方法及利用矩阵快速幂解决递推问题的技术。介绍了通过枚举和数学组合计算特定图结构数量的算法,并通过矩阵快速幂高效求解递推数列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C - Counting Stars
无向图求三元环
这里需要一些小小的改动
题目中要求的A结构其实是两个具有一条公共边的三元环
求这样的个数
根据算法我们一开始就是先枚举一条边,这样我们就以第一条边为公共边,找出三元环的个数,然后从这n个中选2个即可,也就是sum⋅(sum−1)/2

#include<cstdio>
#include<vector>
#include<unordered_set>
#include<cmath>

using namespace std;

typedef long long ll;

const int maxn=1e5+10;

vector<int> g[maxn];
unordered_set<ll> st;

int vis[maxn],link[maxn],out[maxn];

int main(){
    ll ans,sum;
    int n,m,x,y,z,B;
    while(scanf("%d%d",&n,&m)!=EOF){
        B=sqrt(m);
        st.clear();
        for(int i=1;i<=n;i++){
            g[i].clear();
            vis[i]=link[i]=out[i]=0;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            out[x]++;
            g[y].push_back(x);
            out[y]++;
            st.insert((ll)n*x+y);   //对于大于根号下m的边不好这样进行判断时候有边相连,速度快而且更加家简便
            st.insert((ll)n*y+x);
        }
        ans=0;
        for(int i=1;i<=n;i++){
            x=i;
            vis[x]=1;
            for(int j=0;j<g[x].size();j++){
                link[g[x][j]]=x;
            }
            for(int j=0;j<g[x].size();j++){
                sum=0;
                y=g[x][j];          //枚举第二个点,第一条边
                if(vis[y]) continue;
                if(out[y]<=B){
                    for(int k=0;k<g[y].size();k++){
                        z=g[y][k];
                        if(link[z]==x) sum++;
                    }
                }else{
                    for(int k=0;k<g[x].size();k++){
                        z=g[x][k];
                        if(st.find((ll)z*n+y)!=st.end()) sum++;
                    }
                }
                ans+=sum*(sum-1)/2;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D - Covering
通过dfs产生前10个数,列成方程组的形式结合搞死先生的消元法算出四个系数
之所以是4个可能是因为4*n的缘故把。
ac代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<list>
#include<queue>
#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define MAXN 0x3f3f3f3f3f3f3f3f
#define PI acos(-1.0)
#define E exp(1.0)
using namespace std;

const ll MOD=1e9+7;
const int N=4;

struct node{
    ll a[10][10];
}tmp,ans,t;

//#define debug

node matrix(node x,node y ){
    node q;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++){
            q.a[i][j]=0;
            for(int k=1;k<=N;k++){
                q.a[i][j]=(q.a[i][j]+x.a[i][k]*y.a[k][j]+MOD)%MOD;
            }
        }
    }
    return q;
}

void quick_ma(ll n){
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++){
            ans.a[i][j]=0;
        }
    }
    for(int i=1;i<=N;i++) ans.a[i][i]=1;
    t=tmp;
    while(n){
        if(n&1) ans=matrix(ans,t);
        n>>=1;
        t=matrix(t,t);
    }
}

int main()
{
    #ifdef debug
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    #endif // debug

    ll n;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++){
            tmp.a[i][j]=0;
        }
    }
    tmp.a[1][1]=1;
    tmp.a[1][2]=5;
    tmp.a[1][3]=1;
    tmp.a[1][4]=-1;
    tmp.a[2][1]=1;
    tmp.a[3][2]=1;
    tmp.a[4][3]=1;
    while(scanf("%lld",&n)!=EOF){
        if(n==1) printf("1\n");
        else if(n==2) printf("5\n");
        else if(n==3) printf("11\n");
        else if(n==4) printf("36\n");
        else{
            quick_ma(n-4);
            printf("%lld\n",(ans.a[1][1]*36l%MOD+ans.a[1][2]*11l%MOD+ans.a[1][3]*5l%MOD+ans.a[1][4])%MOD);
        }
    }
    return 0;
}
/*
*/

附上搞死先生的消元法“:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=100;
double a[N][N];
double ans[N];
int n;
void swap_r(int q,int p)
{
    for(int i=1;i<=n+1;i++)
    {
        double t=a[p][i];
        a[p][i]=a[q][i];
        a[q][i]=t;
    }
}
void swap_c(int q,int p)
{
    for(int i=1;i<=n+1;i++)
    {
        double t=a[i][p];
        a[i][p]=a[i][q];
        a[i][q]=t;
    }
}
void prt()
{
    for(int i=1;i<=n+1;i++)
    {
        for(int j=1;j<=n+1;j++)
            printf("%2.5f ",a[i][j]);
        printf("\n");
    }
    printf("\n");
}
void gs()
{
    for(int i=1;i<n;i++)
    {
        double m=fabs(a[i][i]);
        int p=i,q=i;
        for(int j=i+1;j<=n;j++)
            for(int k=i;k<=n;k++)
            if(fabs(a[j][k])>m)
            {
                m=fabs(a[j][k]);
                p=j;
                q=k;
            }
            //cout<<p<<endl;
        if(p!=i)
            swap_r(p,i);
        if(q!=i)
            swap_c(q,i);
        //prt();
        for(int j=i+1;j<=n;j++)
        {
            if(a[j][i]==0.0) continue;
            double t=a[j][i]/a[i][i];
            a[j][i]=0.0;
            for(int k=i+1;k<=n+1;k++)
                a[j][k]-=t*a[i][k];
        }
        //prt();
    }
    prt();
}
void red()
{
    for(int i=n;i>1;i--)
    {
        for(int j=i-1;j>=1;j--)
        {
            if(a[j][i]==0) continue;
            double t=a[j][i]/a[i][i];
            a[j][i]=0.0;
            for(int k=i+1;k<=n+1;k++)
                a[j][k]-=t*a[i][k];
        }
    }
    prt();
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
            scanf("%lf",&a[i][j]);
    for(int i=1;i<=n;i++)
        a[n+1][i]=i;
    gs();
    red();
    for(int i=1;i<=n;i++)
        ans[(int)a[n+1][i]]=a[i][n+1]/a[i][i];
    for(int i=1;i<=n;i++)
        printf("x%d=%f\n",i,ans[i]);
    return 0;
}

附上dfs打表的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;


const int N=1e5+10;
bool vis[4][100];
int ans;

void dfs(int i,int j,int n){
    if(i==4){
        ans++;
        return;
    }
    int nj=j+1;
    int ni=i;
    if(nj==n){
        nj=0;
        ni++;
    }
    if(vis[i][j]) dfs(ni,nj,n);
    else{
        if(j!=n-1&&!vis[i][j+1]){
            vis[i][j+1]=vis[i][j]=1;
            dfs(ni,nj,n);
            vis[i][j+1]=vis[i][j]=0;
        }
        if(i!=3&&!vis[i+1][j]){
            vis[i][j]=vis[i+1][j]=1;
            dfs(ni,nj,n);
            vis[i][j]=vis[i+1][j]=0;
        }
    }
}

void make_table(int n){
    for(int i=1;i<=n;i++){
        ans=0;
        dfs(0,0,i);
        printf("%d  %d\n",i,ans);
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    make_table(n);
    return 0;
}

E - CS Course
可以有两种方法来实现。
第一种方法:
通过前后缀和来计算,因为是从中任意删除一个,如果直接进行模拟的话,太浪费时间,而采用前后缀和的方法既简便又又节省时间。
第二种方法:
通过模拟位运算的特点;
因为与运算只要有一个为0则全部为0,或运算只要有一个是1就全部为1,
异或运算,现将全部的数字进行异或运算,然后再拿要删除的那个元素跟最后结果进行异或就能得到答案。
因此在计算与跟或的时候只需要数出每一位上的0跟1的数目,然后再跟要删除的那个数的相应位数进行比较即可。
附代码:

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

int a[100005];
int presumand[100005];
int sufsumand[100005];
int presumor[100005];
int sufsumor[100005];
int presumno[100005];
int sufsumno[100005];

int main(){
    int n,p;
    while(~scanf("%d%d",&n,&p)){
        for(int i=0;i<n;i++){
        scanf("%d",&a[i]);

        }
        //前前前
        presumand[0]=presumor[0]=presumno[0]=a[0];
        for(int i=1;i<n;i++){
            presumand[i]=a[i]&presumand[i-1];
            presumor[i]=presumor[i-1]|a[i];
            presumno[i]=presumno[i-1]^a[i];
        }
        sufsumand[n-1]=sufsumor[n-1]=sufsumno[n-1]=a[n-1];
        for(int i=n-2;i>=0;i--){
            sufsumand[i]=a[i]&sufsumand[i+1];
            sufsumor[i]=sufsumor[i+1]|a[i];
            sufsumno[i]=sufsumno[i+1]^a[i];
        }

        int k;
        while(p--){
            scanf("%d",&k);
            k--;
            if(k==0){
                printf("%d ",sufsumand[k+1]);
                printf("%d ",sufsumor[k+1]);
                printf("%d\n",sufsumno[k+1]);
            }else if(k==n-1){
                printf("%d ",presumand[k-1]);
                printf("%d ",presumor[k-1]);
                printf("%d\n",presumno[k-1]);
            }
            else{
                printf("%d ",presumand[k-1]&sufsumand[k+1]);
                printf("%d ",presumor[k-1]|sufsumor[k+1]);
                printf("%d\n",sufsumno[k+1]^presumno[k-1]);
            }
        }
    }

    return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N];
int cnt[N];
int main(){
    int n,p;
    while(~scanf("%d%d",&n,&p)){
        memset(a,0,sizeof(a));
        memset(cnt,0,sizeof(cnt));
        int ans3=0;
        int max=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            ans3^=a[i];   //异或

            int tmp=a[i];
            int len=0;
            while(tmp){
                if(tmp%2) cnt[len]++;
                tmp>>=1;  //左移
                len++;
                if(max<len)
                    max=len;
            }
        }
        for(int i=0;i<max;i++){ //0的个数
            cnt[i]=n-cnt[i];
        }
        int op;
        for(int i=0;i<p;i++){
            scanf("%d",&op);
            int t=a[op-1];
            int ans1=0,ans2=0;

            for(int j=0;j<max;j++){
                if(cnt[j]==0||(cnt[j]==1&&t%2==0))  //0个数为0的话,则与,或都要运算;   如果0个数为1,而不要的数本位是0,则剩余0个数也为0
                    ans1+=1<<j,
                    ans2+=1<<j;
                else if(n-cnt[j]>1||(n-cnt[j]==1&&t%2==0))
                    ans2+=1<<j;
                t>>=1;
            }

            printf("%d %d %d\n",ans1,ans2,ans3^a[op-1]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值