TopCoder SRM691 Div2

前言

最近打算打一段时间TC和CF。

题目及代码

300

题目很简单,把数字排一下序,然后数字之间挨个贪心插入”+”就行了。

代码:

#include <vector>  
#include <list>  
#include <map>  
#include <set>  
#include <deque>  
#include <stack>  
#include <bitset>  
#include <algorithm>  
#include <functional>  
#include <numeric>  
#include <utility>  
#include <sstream>  
#include <iostream>  
#include <iomanip>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <ctime>  
#include <cstring>  
using namespace std;  
typedef long long LL;
typedef vector <int>    VI;
typedef pair <int,int>  PII;
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
#define pb  push_back
#define mp  make_pair
#define fi  first
#define se  second
#define lrt rt<<1
#define rrt rt<<1|1
#define lson    rt<<1,l,mid
#define rson    rt<<1|1,mid+1,r

int in(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0' || ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}

void out(int a) {
    if(a < 0) { putchar('-'); a = -a; }
    if(a >= 10) out(a / 10);
    putchar(a % 10 + '0');
}

const int maxn = 100;

class Plusonegame  
{  
public:  
string getorder(string s)  
{  
    int cnt = 0;
    string ans = string("");
    char p = '+';
    char str[maxn];
    int len = 0;
    int slen = s.length();
    FOR(i,0,slen){
        if(s[i] == '+') {cnt ++;continue;}
        str[len++] = s[i];
    }
    sort(str,str+len);
    int cur = 0;
    for(int i = 0;i < len;i++){
        int t = str[i]-'0';
        while(cnt && cur < t){
            ans += p;
            cur ++;
            cnt --;
        }
        ans += str[i];
    }
    while(cnt){
        ans += p;
        cnt --;
    }
    return ans;
}  


};  

500

这题画一下图就搞定了,如果一个边的双连通图中的点被选为M点集,那么怎么搞都能形成一个连通图。加入一个边的双连通块中的点有 cnt 个点,那么共可以形成的点集的方式有 2cnt 个。如果不是双连通图中的点,那么只有它连接了双连通图上的点,并且双连通图上的点连接到 n 才能够形成。

公式说得比较明白一点吧,假设原始图是一个连通图,那么这个连通图中最多只可能出现一个双连通分量。假设这个双连通分量有a个点,不属于这个双连通分量但属于这个连通图的其他点有 b 个点。

那么共可以形成的点集数量为:(2a1)(2b)+1(最后这个加1是空集的时候满足条件可以加的)

ps: 由于本题可能有重边,所以需要特判一下。

代码:

#include <vector>  
#include <list>  
#include <map>  
#include <set>  
#include <deque>  
#include <stack>  
#include <bitset>  
#include <algorithm>  
#include <functional>  
#include <numeric>  
#include <utility>  
#include <sstream>  
#include <iostream>  
#include <iomanip>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <ctime>  
#include <cstring>  
using namespace std;  
typedef long long LL;
typedef vector <int>    VI;
typedef pair <int,int>  PII;
#define FOR(i,x,y)  for(int i = (int)x;i < (int)y;++ i)
#define IFOR(i,x,y) for(int i = (int)x;i > (int)y;-- i)
#define pb  push_back
#define mp  make_pair
#define fi  first
#define se  second
#define lrt rt<<1
#define rrt rt<<1|1
#define lson    rt<<1,l,mid
#define rson    rt<<1|1,mid+1,r

const int inf = 1e9+7;
const int N = 55;

int in(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0' || ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}

void out(int a) {
    if(a < 0) { putchar('-'); a = -a; }
    if(a >= 10) out(a / 10);
    putchar(a % 10 + '0');
}

vector <int> G[N];  
stack <int> s;  
int pos[N],dfn[N],dfs_clock,low[N],n,m,ans,num[N];  

void dfs(int u,int fa){  
    dfn[u] = low[u] = ++dfs_clock;  
    s.push(u);  
    FOR(i,0,G[u].size()){  
        int v = G[u][i];  
        if(!dfn[v]){  
            dfs(v,u);  
            low[u] = min(low[u],low[v]);  
        }  
        else if(v != fa){  
            low[u] = min(dfn[v],low[u]);  
        }  
    }  
    if(low[u] == dfn[u]){  
        ans++;  
        int cnt = 0;
        while(!s.empty()){  
            int v = s.top();
            cnt ++;
            s.pop();  
            if(v == u) break;  
        }  
        num[ans-1] = cnt;
    }  
}  

class Sunnygraphs2  
{  
public:  
long long count(vector <int> a)  
{  
    n = (int)a.size();
    FOR(i,0,n)  G[i].clear();
    memset(dfn,0,sizeof(dfn));
    FOR(i,0,n)  low[i] = inf;
    while(!s.empty())   s.pop();
    dfs_clock = 0;  ans = 0;
    memset(num,0,sizeof(num));
    FOR(i,0,n){
        int u = a[i];
        if(a[u] == i && u < i){
            num[ans++] = 2;
            continue;
        }
        G[u].pb(i); G[i].pb(u);
    }
    FOR(i,0,n){
        if(!dfn[i]) dfs(i,-1);
    }
    int tot = 0,tans = 0;
    FOR(i,0,ans) if(num[i] >= 2)
        tot += num[i],tans ++;
    LL res = 1;
    int ress = n-tot;
    FOR(i,0,ans){
        res *= ((1LL << num[i])-1);
    }
    res *= (1LL << ress);
    if(tans == 1)    res ++;
    return res;
}  
};

900

要求不能整除这个数的数的第二小,很容易想到暴力!!!因为可能出现的答案的值很小,不会超过 30 ,暴力枚举 num 第一个不能被整除的数,第二个不能被整除的数,那么 n 的前缀和,只需要算第一小,第二小,对这个前缀和的贡献就可以了。

感觉这里面有一个不错的东西:如何找出第一小,第二小不能整除整个1…n中数的贡献?能做的是整除,处理不整除只需要搞一个容斥就好了,因为是简单的容斥,所以瞎J8写写就好了!!!

复杂度很容易想到o(302)

代码:

#include <vector>  
#include <list>  
#include <map>  
#include <set>  
#include <deque>  
#include <stack>  
#include <bitset>  
#include <algorithm>  
#include <functional>  
#include <numeric>  
#include <utility>  
#include <sstream>  
#include <iostream>  
#include <iomanip>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <ctime>  
#include <cstring>  
using namespace std;  
typedef long long LL;
typedef vector <int>    VI;
typedef pair <int,int>  PII;
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
#define pb  push_back
#define mp  make_pair
#define fi  first
#define se  second
#define lrt rt<<1
#define rrt rt<<1|1
#define lson    rt<<1,l,mid
#define rson    rt<<1|1,mid+1,r

int in(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0' || ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}

void out(int a) {
    if(a < 0) { putchar('-'); a = -a; }
    if(a >= 10) out(a / 10);
    putchar(a % 10 + '0');
}

const int maxn = 30;

LL lcm[maxn][maxn],nd[maxn][maxn][3];

LL Lcm(LL x,LL y)   {return x/__gcd(x,y)*y;}

void init(){
    lcm[1][1] = 1;
    FOR(i,1,maxn)   FOR(j,1,maxn)   lcm[i][j] = 1;
    FOR(i,1,maxn){
        lcm[i][i] = i;
        FOR(j,i+1,maxn){
            LL tem = __gcd(lcm[i][j-1],(LL)j);
            lcm[i][j] = lcm[i][j-1]/tem*j;
        }
    }
    FOR(i,2,maxn){
        FOR(j,i+1,maxn){
            nd[i][j][0] = Lcm(lcm[1][i-1],lcm[i+1][j-1]);
            nd[i][j][1] = Lcm(lcm[1][i-1],lcm[i+1][j]);
            nd[i][j][2] = lcm[1][j-1];
        }
    }
}

class Undiv2  
{  
public:  

long long calc(int n){
    LL ans = 0;
    FOR(i,2,maxn){
        FOR(j,i+1,maxn){
            LL tem = 0;
            tem = j*(n/nd[i][j][0]-n/nd[i][j][1]-n/nd[i][j][2]+n/lcm[1][j]);
            ans += tem;
        }
    }
    return ans;
}
long long getsum(int n)  
{  
    init();
    return calc(n);
}  
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值