2017-2018 ACM-ICPC, Asia Daejeon Regional Contest

题目传送门

只打了三个小时。

A. Broadcast Stations

 

B. Connect3

补题:zz

题解:因为格子是4*4的,而且每次落子的位置最多是只有四个,再加上剪枝,情况不会很多,直接爆搜就行了,再用三进制记录已经合法的情况,去掉重复的情况就行了。(用vs2017交会ac,但c++17会wa1,很奇怪)

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<math.h>
#include<cmath>
#include<time.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<numeric>
#include<stack>
#include<bitset>
#include<unordered_map>
const int maxn = 0x3f3f3f3f;
const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427;
const double PI = 3.141592653589793238462643383279;
//#ifdef TRUETRUE
//#define gets gets_s
//#endif
using namespace std;
int a, b;
int ans;
map<int, int>mp;
bool Judge(int c[10][10], int aa, int bb, int pos)
{
    if (aa == a && bb == b && pos == 1)
    {
        return false;
    }
    int cc[10][10], i, j;
    for (i = 1; i <= 4; i++)
    {
        for (j = 1; j <= 4; j++)
        {
            cc[i + 1][j + 1] = c[i][j];
        }
    }
    aa++;
    bb++;
    bool flag = false;
    if (cc[aa][bb] == cc[aa - 1][bb] && cc[aa][bb] == cc[aa - 2][bb]
        || cc[aa][bb] == cc[aa + 1][bb] && cc[aa][bb] == cc[aa + 2][bb]
        || cc[aa][bb] == cc[aa][bb - 1] && cc[aa][bb] == cc[aa][bb - 2]
        || cc[aa][bb] == cc[aa][bb + 1] && cc[aa][bb] == cc[aa][bb + 2]
        || cc[aa][bb] == cc[aa + 1][bb + 1] && cc[aa][bb] == cc[aa + 2][bb + 2]
        || cc[aa][bb] == cc[aa - 1][bb - 1] && cc[aa][bb] == cc[aa - 2][bb - 2]
        || cc[aa][bb] == cc[aa - 1][bb] && cc[aa][bb] == cc[aa + 1][bb]
        || cc[aa][bb] == cc[aa][bb - 1] && cc[aa][bb] == cc[aa][bb + 1]
        || cc[aa][bb] == cc[aa - 1][bb - 1] && cc[aa][bb] == cc[aa + 1][bb + 1]
        || cc[aa][bb] == cc[aa - 1][bb + 1] && cc[aa][bb] == cc[aa - 2][bb + 2]
        || cc[aa][bb] == cc[aa + 1][bb - 1] && cc[aa][bb] == cc[aa + 2][bb - 2]
        || cc[aa][bb] == cc[aa - 1][bb + 1] && cc[aa][bb] == cc[aa + 1][bb - 1])
    {
        flag = true;
    }
    if (flag && pos == 2 && aa - 1 == a && bb - 1 == b)
    {
        int tmp = 0;
        for (i = 1;i <= 4;i++)
        {
            for (j = 1;j <= 4;j++)
            {
                tmp *= 3;
                tmp += c[i][j];
            }
        }
        if (!mp[tmp])
        {
            ans++;
            mp[tmp] = 1;
        }
        return false;
    }
    if (flag || (!flag && aa - 1 == a && bb - 1 == b))
    {
        return false;
    }
    else
    {
        return true;
    }
}
void f(int c[10][10], int d[10], int pos)
{
    int i;
    for (i = 1; i <= 4; i++)
    {
        if (d[i] <= 4)
        {
            c[d[i]][i] = pos;
            int dd[10][10];
            for (int ii = 1;ii <= 4;ii++)
            {
                memcpy(dd[ii], c[ii], sizeof(c[ii]));
            }
            if (Judge(dd, d[i], i, pos))
            {
                d[i]++;
                f(dd, d, 3 - pos);
                d[i]--;
            }
            c[d[i]][i] = 0;
        }
    }
}
void init(void)
{
    mp.clear();
}
int main(void)
{
    //ios::sync_with_stdio(false);
    int x;
    while (~scanf("%d %d %d", &x, &a, &b))
    {
        init();
        int c[10][10];
        int d[10] = { 1,1,1,1,1,1,1,1,1,1 };
        d[x]++;
        memset(c, 0, sizeof(c));
        c[1][x] = 1;
        ans = 0;
        f(c, d, 2);
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 

 

C. Game Map

 题意:给定一张无向图,要求找出一个最长的序列,使得这个序列的度数是严格递增的。

思路:对于每一个点来说,这个点在序列中的位置可以是1,也可以是从和它相邻的且读书小于它的点转移过来的,所以枚举度数从小到大的点即可。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=100100;
vector<int>ve[100010];
unordered_map<int,int>mp[100010];
int c[100010],de[100010];
struct s
{
    int de,id;
}z[100010];
inline bool comp(s a,s b)
{
    return a.de <b.de;
}
int main(){
    int n,m,i,z1,z2,si,j,ans;
    while(~scanf("%d %d",&n,&m))
    {
        for(i = 0;i <= n;i++)
        {
            ve[i].clear();
            mp[i].clear();
            c[i] = 1;
            z[i].de = 0;
            z[i].id = i;
            de[i] = 0;
        }
        for(i = 0;i < m;i++)
        {
            scanf("%d %d",&z1,&z2);
            if(z1 > z2)
            {
                int r = z1;
                z1 = z2;
                z2 = r;
            }
            if(z1 != z2 && !mp[z1][z2])
            {
                mp[z1][z2] = 1;
                ve[z1].push_back(z2);
                ve[z2].push_back(z1);
                z[z1].de++;
                z[z2].de++;
                de[z1]++;
                de[z2]++;
            }
        }
        sort(z,z + n,comp);
        ans = 1;
        for(i = 0;i < n;i++)
        {
            si = ve[z[i].id].size();
            //printf(" %d %d\n",z[i].id,z[i].de);
            for(j = 0;j < si;j++)
            {
                //printf("   %d %d %d\n",z[i].de,z[ve[z[i].id][j]].de,ve[z[i].id][j]);
                if(z[i].de > de[ve[z[i].id][j]])
                {
                    c[z[i].id] = max(c[z[i].id],c[ve[z[i].id][j]] + 1);
                }
            }
            //printf("          %d\n",c[z[i].id]);
            ans = max(ans,c[z[i].id]);
        }
        printf("%d\n",ans);
    }
}
View Code

 

D. Happy Number

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#define ll long long
#define maxn 4001000
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,len;
map<int,int> mp;
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    scanf("%d",&n); 
    len=1;
    mp[n]=1;
}
void work()
{
    while(1)
    {
        int x=n,ans=0;
        while(x>0)
        {
            ans+=(x%10)*(x%10);
            x/=10; 
        }
        if(ans==1) 
        {
            printf("HAPPY");
            return;
        }
        if(mp[ans]!=0) 
        {
            printf("UNHAPPY");
            return;
        }
        else mp[ans]=++len;
        n=ans;
    }
}
int main()
{
    init();
    work();
}
View Code

 

E. How Many to Be Happy

补题:kk

  一开始想了个假算法,没被队友hack,然后wa3?最近好像经常写假算法

  对于这样最小生成树的题的选边问题,我们一定要考虑克鲁斯卡尔的过程,对于一条边权为$w$的边,如果他要在最小生成树上,那么前提条件是权值比他小的边不会把u到v所属的两个结合连接在一起,也就是说,现在我们要去掉一些权值小于$w$的边,让u和v分在不同的两个集合里面,想到这里就会想到最小割的模型,对于每条边,都把权值小于他的边加入到图里,然后求一个最小割就可以了。

  所以,最小生成树的题一定要考虑克鲁斯卡尔!这真是一个神奇的算法,还有对数据范围要敏感,$n=100$也应该想到网络流。

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

const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int maxn = 510;

struct Edge {
    int to, flow, nxt;
    Edge(){}
    Edge(int to, int nxt, int flow):to(to),nxt(nxt), flow(flow){}
}edge[maxn << 2];

int head[maxn], dep[maxn];
int S, T;
int N, n, m, tot,ans;

void Init(int n)
{
    N = n;
    for (int i = 0; i < N; ++i) head[i] = -1;
    tot = 0;
}

void addv(int u, int v, int w, int rw = 0)
{
    edge[tot] = Edge(v, head[u], w); head[u] = tot++;
    edge[tot] = Edge(u, head[v], rw); head[v] = tot++;
}

bool BFS()
{
    for (int i = 0; i < N; ++i) dep[i] = -1;
    queue<int>q;
    q.push(S);
    dep[S] = 1;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edge[i].nxt)
        {
            if (edge[i].flow && dep[edge[i].to] == -1)
            {
                dep[edge[i].to] = dep[u] + 1;
                q.push(edge[i].to);
            }
        }
    }
    return dep[T] < 0 ? 0 : 1;
}

int DFS(int u, int f)
{
    if (u == T || f == 0) return f;
    int w, used = 0;
    for (int i = head[u]; ~i; i = edge[i].nxt)
    {
        if (edge[i].flow && dep[edge[i].to] == dep[u] + 1)
        {
            w = DFS(edge[i].to, min(f - used, edge[i].flow));
            edge[i].flow -= w;
            edge[i ^ 1].flow += w;
            used += w;
            if (used == f) return f;
        }
    }
    if (!used) dep[u] = -1;
    return used;
}

int Dicnic()
{
    int ans = 0;
    while (BFS())
    {
        ans += DFS(S, INF);
    }
    return ans;
}
struct node{
    int u,v,w;
    friend bool operator<(const node &a,const node &b)
    {
        return a.w<b.w;
    }
}a[maxn];
int main(){
    while(cin>>n>>m)
    {
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
        }
        ans=0;
        sort(a+1,a+1+m);
        for(int i=1;i<=m;i++)
        {
            Init(n+1);
            for(int j=1;j<i;j++)
            {
                if(a[j].w>=a[i].w)break;
                addv(a[j].u,a[j].v,1);
                addv(a[j].v,a[j].u,1);
            }
            S=a[i].u,T=a[i].v;
            ans+=Dicnic();
    //        printf("debug\n");
        }
        printf("%d\n",ans);
    }
}
View Code

 

F. Philosphoer's Walk

 题意:给定一张题目里描述的规模的图,输出走k布后的点。

思路:每一张图都能分解为四张规模一样的图,判断最终会在哪一张小图里和起止点的位置,之后再重复这个操作,直到2*2的图为止。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=510;
struct s
{
    int a,b;
}in;
int Pow[110];
int Node[5][2] = {0,0,1,1,1,2,2,2,2,1};
inline void init(void)
{
    int i,tmp = 1;
    for(i = 0;i <= 30;i++)
    {
        Pow[i] = tmp;
        tmp <<= 1;
    }
}
inline s f(int num,int step)
{
    int pos,st;
    s jk;
    if(num == 1)
    {
        s tmp;
        tmp.a = Node[step][0];
        tmp.b = Node[step][1];
        return tmp;
    }
    pos = (step - 1) / (Pow[num - 1] * Pow[num - 1]);
    st = (step - 1) % (Pow[num - 1] * Pow[num - 1]) + 1;
    s nex = f(num - 1,st);
    if(pos == 0)
    {
        jk.a = 0 + nex.b;
        jk.b = 0 + nex.a;
    }
    else if(pos == 1)
    {
        jk.a = 0 + nex.a;
        jk.b = Pow[num - 1] + nex.b;
    }
    else if(pos == 2)
    {
        jk.a = Pow[num - 1] + nex.a;
        jk.b = Pow[num - 1] + nex.b;
    }
    else
    {
        jk.a = Pow[num] + 1 - nex.b;
        jk.b = Pow[num - 1] + 1 - nex.a;
    }
    return jk;
}
int main(){
    int n,m,i;
    init();
    while(~scanf("%d %d",&n,&m))
    {
        for(i = 1;i <= 30;i++)
        {
            if(Pow[i] == n)
            {
                break;
            }
        }
        in = f(i,m);
        printf("%d %d\n",in.a,in.b);
    }
}
View Code

 

G. Rectilinear Regions

 

H. Rock Paper Scissors

 题解:FFT,把s1串中石头剪刀布的位置记录下来,再把S2串中石头剪刀布的位置记录下来。分输赢做三次FFT。

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define maxn (1<<18)
#define pi 3.141592653589793238462643383
using namespace std;

struct complex
{
    double re,im;
    complex(double r=0.0,double i=0.0) {re=r,im=i;}
    void print() {printf("%.lf ",re);}
} a[maxn*2],b[maxn*2],W[2][maxn*2];

int N,na,nb,rev[maxn*2],n,m;
char s1[101010],s2[101010];
double c[maxn*2],ans;
complex operator +(const complex&A,const complex&B) {return complex(A.re+B.re,A.im+B.im);}
complex operator -(const complex&A,const complex&B) {return complex(A.re-B.re,A.im-B.im);}
complex operator *(const complex&A,const complex&B) {return complex(A.re*B.re-A.im*B.im,A.re*B.im+A.im*B.re);}

void FFT(complex*a,int f)
{
    complex x,y;
    for (int i=0; i<N; i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for (int i=1; i<N; i<<=1)
        for (int j=0,t=N/(i<<1); j<N; j+=i<<1)
            for (int k=0,l=0; k<i; k++,l+=t) x=W[f][l]*a[j+k+i],y=a[j+k],a[j+k]=y+x,a[j+k+i]=y-x;
    if (f) for (int i=0; i<N; i++) a[i].re/=N;
}

void work()
{
    for (int i=0; i<N; i++)
    {
        int x=i,y=0;
        for (int k=1; k<N; x>>=1,k<<=1) (y<<=1)|=x&1;
        rev[i]=y;
    }
    for (int i=0; i<N; i++) W[0][i]=W[1][i]=complex(cos(2*pi*i/N),sin(2*pi*i/N)),W[1][i].im=-W[0][i].im;
}
void doit()
{
    work(),FFT(a,0),FFT(b,0);
    for (int i=0; i<N; i++) a[i]=a[i]*b[i];
    FFT(a,1);
    for (int i=0; i<na+nb-1; i++) 
    {
        c[i]+=a[i].re;
        if(i>=m-1) ans=max(ans,c[i]);
        //printf("%d %d\n",i,c[i]);
    }
}

void popo()
{   
    scanf("%d%d",&n,&m);
    scanf("%s",s1);
    scanf("%s",s2);
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    na=n;nb=m;
    for(int i=0;i<n;i++)    
        if(s1[i]=='R') a[i].re=1;
    for(int i=0;i<m;i++)
        if(s2[i]=='P') b[m-i-1].re=1;
    for (N=1; N<na||N<nb; N<<=1); N<<=1;
    doit();
    //printf("------\n");
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    for(int i=0;i<n;i++)    
        if(s1[i]=='P') a[i].re=1;
    for(int i=0;i<m;i++)
        if(s2[i]=='S') b[m-i-1].re=1;
    for (N=1; N<na||N<nb; N<<=1); N<<=1;
    doit();
    //printf("------\n");
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    for(int i=0;i<n;i++)    
        if(s1[i]=='S') a[i].re=1;
    for(int i=0;i<m;i++)
        if(s2[i]=='R') b[m-i-1].re=1;
    for (N=1; N<na||N<nb; N<<=1); N<<=1;
    doit();
}


int main()
{
    popo();
    printf("%.lf\n",ans);
}
View Code

 

I. Slot Machines

补题kk

  我们把这个数组倒过来,现在变成了我们要去掉末尾的一段,使得前面部分循环,并且不完整的循环节在数列的前方。对于两段循环来说,开头一部分肯定是一样的(否则怎么能叫循环呢),如果有一段循环不完整,那么开头一部分肯定是一样的,而这里循环的开头就是倒过来的数组的前缀,后面是一个完整的数组,前缀和后缀相同,我们想到了什么呢?kmp!

  但是这里我们只需要$fail$就可以了。

  先对倒过来的数组$fail$一遍,得到$f$数组,然后循环长度就变成了$i-f[i]$,对此我们稍微证明一下为什么。

  对于$i-f[i]>=f[i]$,也就是相等的前缀和后缀不重叠,那么这必然是一个循环长度。

  对于$i-f[i]<f[i]$,也就是相等的前缀喝后缀是重叠的,那么我们就要稍微理解一下,设$len=i-f[i]$那么必然有$s{f[i]->f[i]+len]}==s{f[i]-len->f[i]}$,然后一步一步的画过来,这个也是循环节(这部分还是画图好理解些)

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1000010;
int n;
int a[maxn],f[maxn];
void fail(){
    f[0]=-1;
    for(int j=1;j<n;j++)
    {
        for(int i=f[j-1];;i=f[i])
        {
            if(a[j]==a[i+1]){
                f[j]=i+1;
                break;
            }else if(i==-1)
            {
                f[j]=-1;
                break;
            }
        }
    }
}
int main(){
    while(cin>>n)
    {
        for(int i=n-1;i>=0;i--)
        {
            scanf("%d",&a[i]);
        }
        fail();
        int k=n,p=1;
        for(int i=0;i<n;i++)
        {
            int kk=n-i-1;
            int pp=i-f[i];
            if(kk+pp<k+p){
                k=kk,p=pp;
            }else if(kk+pp==k+p&&pp<p){
                k=kk,p=pp;
            }
        }
        printf("%d %d\n",k,p);
        
    }
}
View Code

 

 

J. Strongly Matchable

 

K. Untangling Chain

 

L. Vacation Plans

posted @ 2019-03-09 22:29  光芒万丈小太阳  阅读(194)  评论(0编辑  收藏  举报