ACM-ICPC 2018 南京赛区网络预赛A B E J

/************  A.An Olympian Math Problem  ********************

题意:输入一个n,用S来表示S=1*1!+2*2!+······+(n-1)*(n-1)!,求S%n的结果
思路:打表找规律,列出前几项会发现ans=n-1,注意要开long long;

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

int main()
{
    int t;
    cin>>t;
    while(t--){
        ll n;
        cin>>n;
        cout<<n-1<<endl;
    }
    return 0;
}
/*********************  B. The writing on the wall  **************************

题意:有一个n*m的矩阵,里面有一些黑矩阵,问这个矩阵有多少个不带黑矩阵的矩阵
思路:把每个小的矩阵都当作一个子矩阵的一个右下角,暴力搜索每个充当右下角的子
矩阵可以存在多少个不含黑块的子矩阵,对于每一列,子矩阵个数应该是之前遍历的
最短的高度累加起来,然后对于每个小矩阵求和

一个三层的for循环遍历整个矩阵,时间复杂度为O(n*m*m);
对于第一层循环,遍历n列的元素,表示为i;
对于第一个二层循环,表示对于该列元素对于每个位置所能够达到的最大高度;
对于第二个二层循环,遍历这列元素的每一个点(即当做右下角的矩阵)
对于第三层循环,即表示这以这个小矩形为右下角,遍历剩下高度的所有情况之和

ll ans=0;
for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++)
        if(Map[i][j]) up[j]=i;
    for(int j=1;j<=m;j++){
        int Min=0x3f3f3f3f;
        for(int k=j;k>0;k--){
            Min=min(Min,i-up[k]);
            ans+=(ll)Min;
        }
    }
}

****************************************************************************/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define met(a,b) memset(a,b,sizeof(a))

int Map[100005][105],up[105];

int main()
{
    int t,cas=0;
    scanf("%d",&t);
    while(t--){
        int n,m,k;
        met(Map,0);
        met(up,0);
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<k;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            Map[x][y]=1;
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                if(Map[i][j]) up[j]=i;
            for(int j=1;j<=m;j++){
                int Min=0x3f3f3f3f;
                for(int k=j;k>0;k--){
                    Min=min(Min,i-up[k]);
                    ans+=(ll)Min;
                }
            }
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}
/*************************   E. AC Challenge  *******************************

题意:有n道题目,每道题目对应有ai和bi两个分值,用time*ai+bi表示这个题目的
分值,其中time表示对应的第几个做这个题,每道题还有对应的前导题,即写这个
题目之前必须写的题目
思路:状压dp
    1.这个题目首先是二进制优化,2^20大概1e6的样子,每个题目有m道前导题,可以
先利用0不需要做1需要做来记录这个题目是否可以做以及题目做过之后的状态
    2.首先是输入以及预处理这个题可以做的前提条件,a b数组分别储存每个题目对应的
分数,d数组用一个n位的二进制表示做这道题目的前提条件,即d[i]+=1<<(x-1);
    3.定义一个最大的可能情况 int now=(1<<n)-1;此时为可能情况的最大值
    4.一个从0到now的循环,当(dp[i]==0&&i!=0)时,表示dp[i]没有被更新过,所以一定
不需要此状态,continue继续;
    5.第二层循环遍历n道题目,如果可以做,把dp[i+(1<<j)]更新掉,此处为状态转移
方程,使得其步步最优dp[i+(1<<j)]=max(dp[i+(1<<j)],dp[i]+time(i)*a[j]+b[j]);
    6.循环结束之后,求出dp数组中的最大值即为最优答案

****************************************************************************/
#include<bits/stdc++.h>
using namespace std;
#define maxn 1<<20
#define ll long long

ll dp[maxn];
int a[25],b[25],d[25];

int time(int x)
{
    int cnt=0;
    while(x){
        x-=x&-x;
        cnt++;
    }
    return cnt+1;
}

int main()
{
    int n,m;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d%d",&a[i],&b[i],&m);
        while(m--){
            int x;
            scanf("%d",&x);
            d[i]+=1<<(x-1);
        }
    }
    int now=(1<<n)-1;
    for(int i=0;i<=now;i++){
        if(dp[i]==0&&i!=0) continue;
        for(int j=0;j<n;j++){
            if((i&(1<<j))==0&&(i&d[j])==d[j]){
                dp[i+(1<<j)]=max(dp[i+(1<<j)],dp[i]+time(i)*a[j]+b[j]);
            }
        }
    }
    ll ans=0;
    for(int i=0;i<=now;i++){
        ans=max(dp[i],ans);
    }
    printf("%lld\n",ans);
    return 0;
}
/******************   J. Sum  *****************************

题意:f(n)定义为n=a*b这样分解的式子,其中a和b不能包含平方因子。
当a!=b时n=a*b和n=b*a算两个式子。t组测试,求f(1)+f(2)+f(3)+···+f(n)

题解:有如下结论
    1.若i是素数则f(i)=2;
    2.若i的某个质数因子个数大于等于2,则f(i)=0;就是说当某个因子个数
大于等于2的时候,所分解的a*b或b*a必定有一个数含有平方因子,即f(i)=0;
    3.若i=a*b且a和b不含相同因子,则f(i)=f(a)*(b);
    4.若i的质因子x的个数为2,f(i)=f(i/(x*x))即为去掉平方因子个数
    打表需要用欧拉筛法

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

const int maxn = 2e7+5;
bool vis[maxn]; //标记合数为true质数为false
int prime[maxn];//prime[0]存个数,后边存连续的质数
int ans[maxn];//结果

void solve()
{
    ans[1]=1;
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            prime[++prime[0]]=i;
            ans[i]=2;//情况1
        }
        for(int j=1;j<=prime[0]&&prime[j]*i<maxn;j++){
            int x=prime[j]*i;
            vis[x]=true;
            if(i%prime[j]) ans[x]=ans[prime[j]]*ans[i];//情况3
            else{
                if(i%(prime[j]*prime[j])==0) ans[x]=0;//情况2
                else ans[x]=ans[x/(prime[j]*prime[j])];//情况4
                break;//欧拉筛法保证每个合数只被它的最小质因子筛去,因此要跳出
            }
        }
    }
    //前缀和
    for(int i=1;i<maxn;i++){
        ans[i]+=ans[i-1];
    }
}

int main()
{
    solve();
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        printf("%d\n",ans[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LeBron_Yang/article/details/82429005