Codeforces Round #311 (Div. 2)

D

分4种情况讨论

1 不需要加边 , 就是说原本就有一个奇数环,我们只要跑一次二分图就好了

2 加一条边 , 也就是说存在大于等于3个点的联通块 我们对于这个联通块也跑一遍二分图, 可以知道图中所有的 同颜色染色中的点任意相连,都是一个奇数环,那么对于每个联通分量都有相应的数可以计算

3 加两条边,也就是说没有大于两个点的联通块存在,并且有两个点的联通块存在,答案也是可以计算出来的 e*(n-2)

4 加三条边 那么就可以知道每个联通块只有一个点,答案是 n*(n-1)*(n-2)/6;

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=100005;
typedef long long LL;
int fa[maxn],Enum[maxn];
pair<int,int>e[maxn];
int fin(int a)
{
     return fa[a]=(a==fa[a])?a:fin( fa[a] );
}
bool solve1(LL n,int m)
{
    for(int i=0; i<m; i++)
        {
             int a=e[i].first,b=e[i].second;
             a=fin(a); b=fin(b);
             if(a==b)continue;
             Enum[a]+=Enum[b];
             fa[b]=a;
        }
    LL num1=0,num2=0;
    for(int i=1; i<=n; i++)
        {
            if(fa[i]!=i)continue;
            if(Enum[i]>2)return false;
            if(Enum[i]==1)num1++;
            else num2++;
        }
     if(num2==0){
        LL d=n*(n-1)*(n-2)/6;
        printf("3 %I64d\n",d);
     }else{
        LL d=num2*(n-2);
        printf("2 %I64d\n",d);
     }
     return true;
}
int H[maxn],nx[maxn*2],to[maxn*2],numofedg;
void add(int a, int b){
    numofedg++; to[numofedg]=b; nx[numofedg]=H[a]; H[a]=numofedg;
    numofedg++; to[numofedg]=a; nx[numofedg]=H[b]; H[b]=numofedg;
}
int color[maxn],num1,num2;
bool bipartite(int u)
{
    if(color[u]==1)num1++;
    else num2++;
    for(int i=H[u]; i; i=nx[i])
    {
          int v=to[i];
          if(color[u]==color[v])return false;
          if(color[v]==0){
            color[v]=3-color[u];
            if(bipartite(v)==false)return false;
          }
    }
    return true;
}
void solve2(LL n, int m)
{
     for(int i=0; i<m; i++){
        add(e[i].first,e[i].second);
     }
     LL ans=0;
     memset(color,0,sizeof(color));
     for(int i=1; i<=n; i++)
        {
             if(color[i]==0){
                color[i]=1;
                num1=num2=0;
                if(bipartite(i)==false){
                    printf("0 1\n");return ;
                }else{
                   ans+= 1LL*num1*(num1-1)/2 +1LL*num2*(num2-1)/2;
                }
             }
        }
        printf("1 %I64d\n",ans);
}
int main()
{
   int n,m;
   scanf("%d%d",&n,&m);
   numofedg=0;
   for(int i=1; i<=n; i++){fa[i]=i,Enum[i]=1; H[i]=0; }
   for(int i=0; i<m; i++)
    {
      scanf("%d%d",&e[i].first,&e[i].second);
    }
    if(solve1(n,m)){return 0;}
    solve2(n,m);
    return 0;
}
View Code

E

题意 给了一个长度为5000的字符串  定义了奇回文 就是   i为奇数, 然后求出字符串的所有子串中 字典序第k大的那个并且是奇回文

我们用dp[i][j] 计算出 以i为起点,j为终点的字符串是否为奇回文,这个很好转移

然后我们将s[i----n]的字符串插入一棵字典树中 需要插入的字符有n个 ,然后对于每个dp[i][j] 是奇回文的串我们就在那个节点上加个1 然后就可以计算出最后的和值了

因为他说的是所有组成 由ab组成 ok那就很好解了 

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=5005;
int dp[maxn][maxn],numofnode=0;
char s[maxn];
struct node
{
   int ch[2];
   int S,Sloc,LS,RS;
   void init(int cSloc, int cLS, int cRS){
       Sloc=cSloc; LS=cLS; RS=cRS;
       S=Sloc+LS+RS;
   }
   void maintain()
   {
        S=Sloc+LS+RS;
   }
}T[maxn*3000];
void insert(int root,int len,int i,int j)
{
    if(j==len){
            T[root].maintain();
            return;
    }
    int id=s[j]-'a';
    if(T[root].ch[id]==0){
        T[root].ch[id]=++numofnode;
        T[numofnode].ch[0]=T[numofnode].ch[1]=0;
        T[numofnode].init(0,0,0);
    }
    int u=T[root].ch[id];
    if(dp[i][j]) T[u].Sloc++;
    insert(u,len,i,j+1);
    T[root].LS=T[ T[root].ch[0] ].S;
    T[root].RS=T[ T[root].ch[1] ].S;
    T[root].maintain();
}
void find(int root, int k)
{
     if(T[root].Sloc>=k)return ;
     k-=T[root].Sloc;
     if(T[root].LS>=k){
        printf("a");
        find(T[root].ch[0],k); return ;
     }
     k-=T[root].LS;
     printf("b");
     find(T[root].ch[1],k);
}
int main()
{
    T[0].S=T[0].Sloc=0;
    T[1].init(0,0,0);
    T[1].ch[0]=T[1].ch[1]=0;
    numofnode=1;
    scanf("%s",s);
    int len=strlen(s);
     for(int i=1; i<=len; i++)
        for(int j=0; j<len; j++)
        {
            if(i+j>len)break;
            if( i == 1 )
               dp[ j ][ i + j - 1 ] = true;
            else if( i == 2 || i == 3 || i == 4 )
            {
                if(s[ j ] == s[ i + j - 1 ]){
                    dp[ j ][ i + j-  1 ]=true;
                }else{
                    dp[ j ][ i + j - 1 ]=false;
                }
            }else
            {
                if(s[j]==s[i+j-1]){
                    dp[j][i+j-1]=dp[j+2][i+j-3];
                }else{
                    dp[j][i+j-1]=false;
                }
            }

        }
     for(int i=0; i<len; i++)
        {
             insert(1,len,i,i);
        }
     int k;
     scanf("%d",&k);
     find(1,k);
    return 0;
}
View Code

 

 

 

posted @ 2015-09-15 22:31  来自大山深处的菜鸟  阅读(163)  评论(0编辑  收藏  举报