2016年中国大学生程序设计竞赛(合肥)-重现赛

A.传递

赛后AC数最多的2016合肥题。赛时没看到是竞赛图,以为是o(n^3)果断放弃

后面才看到。。。一开始想到用补图的2个方向去补原图再判圈,光荣TLE--边数也太多了吧!

后用其他人的判传递法才成功控制时间AC

感觉如果非要判圈,有些数据可以卡dfs

判传递也太简单了。。。从一个点开始覆盖点,如果有2边链且不被传递,到那边的点,会发现还没覆盖,标上2,退出走人

#include <bits/stdc++.h>

using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
const db eps=1e-6;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define CLR(a, b) memset(a, b, sizeof(a))

const int N=5000,M=7000000;//2500*2,2500*2499/2*2
int head[N],cnt,n;
int to[M],nxt[M];
char vis[N];

void addedge(int u,int v)
{
    to[cnt]=v;nxt[cnt]=head[u];head[u]=cnt++;
}
int que[N];
bool bfs(int u)
{
    memset(vis,0,sizeof(vis));
    int tal=0,hed=0;
    que[tal++]=u;
    while(tal!=hed)
    {
        int u=que[hed++];
        for(int i=head[u];i!=-1;i=nxt[i])
        {
            int v=to[i];
            if(vis[v])continue;
            vis[v]=vis[u]+1;que[tal++]=v;
            if(vis[v]>=2)return true;
        }
    }
    return false;
}
bool Bfs(int s)
{
    for(int i=0;i<n+n;i++)
    {
        if(bfs(i))return true;
    }
    return false;
}
char str[N];
int main()
{
    int t,i,j,m,p;
    scanf("%d",&t);
    for(int h=0;h<t;h++)
    {
        scanf("%d",&n);getchar();
        memset(head,-1,sizeof(head));cnt=0;
        for(i=0;i<n;i++)
        {
            gets(str);
            for(j=0;j<n;j++)
            {
                if(str[j]=='P')addedge(i,j);
                else if(str[j]=='Q')addedge(n+i,n+j);
            }
        }
        if(Bfs(0))
        {
            puts("N");
        }
        else
        {
            puts("T");
        }
    }
    return 0;
}
View Code

C.朋友

模拟此套题时以为离根隔偶数条边的边要算sg值,反之不要

结果这个思路是错的。。。还以为此题有多大的复杂度呢

再做就知道了,不管根结点与1边隔了多少0边,只要中间隔了0边,要动这条1边,要做偶数次才能消除此链上的所有1边

所以加边只是往边的两侧反转标记,标记是奇数说明操作奇数次,G赢;否则就是偶数次,B赢

单纯的找规律题

#include <bits/stdc++.h>

using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
const db eps=1e-6;
const int M=100010;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define CLR(a, b) memset(a, b, sizeof(a))

const int N=40010;
set<int> s[N];
char fag[N];
int main()
{
    int t,i,j,n,m,p;
    scanf("%d",&t);
    for(int h=0;h<t;h++)
    {
        memset(fag,0,sizeof(fag));
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)s[i].clear();
        for(i=0;i<n-1;i++)
        {
            int u,v,fl;
            scanf("%d%d%d",&u,&v,&fl);u--,v--;
            if(fl==1)fag[v]^=1,fag[u]^=1,s[u].insert(v),s[v].insert(u);
        }
        for(i=0;i<m;i++)
        {
            int op,x,y,z;
            scanf("%d",&op);
            if(op==0)
            {
                scanf("%d",&x);x--;
                if(fag[x]==1)puts("Girls win!");
                else puts("Boys win!");
            }
            else
            {
                scanf("%d%d%d",&x,&y,&z);x--,y--;
                set<int>::iterator sit1=s[x].find(y);
                set<int>::iterator sit2=s[y].find(x);
                if(sit1!=s[x].end() && z==0)
                {
                    s[x].erase(sit1);s[y].erase(sit2);
                    fag[x]^=1,fag[y]^=1;
                }
                if(sit1==s[x].end() && z==1)
                {
                    s[x].insert(y);s[y].insert(x);
                    fag[x]^=1,fag[y]^=1;
                }
            }
        }
    }
    return 0;
}
View Code

D.平行四边形

(本渣不会用几何画板,用的画图请见谅)下见解析几何部分:

顺带吐槽一句:你们转发x姐的题解不知道自己算吗

E.扫雷

表示还是太简单了。。转移状态什么的就搞定了

可以用上1行有多少个雷来转移系数

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e4 + 5;
const LL mod = 100000007;
LL dp[maxn];
char ch[maxn];
int a[maxn];
int n;
int p[3] = {1, 2, 1};

LL solve(){
    LL ans = 0;
    for (int i = 0; i <= 2 && i <= a[1]; i++){
        if (a[1] - i > 2) continue;
        dp[1] = a[1] - i;
        for (int j = 2; j <= n; j++){
            dp[j] = a[j - 1] - dp[j - 1] - dp[j - 2];
        }
        if (dp[n] + dp[n - 1] != a[n]) continue;
        LL res = 1;
        for (int j = 1; j <= n; j++){
            res = res * p[dp[j]];
            if (res > mod) res %= mod;
        }
        ans = (ans + res) % mod;
    }
    return ans;
}

int main(){
    int t; cin >> t;
    while (t--){
        scanf("%s", ch);
        n = strlen(ch);
        bool flag = true;
        for (int i = 0; i < n; i++){
            if ((i == 0 || i == n-1) && ch[i] > '4'){
                flag = false; break;
            }
            if (ch[i] > '6'){
                flag = false; break;
            }
            a[i + 1] = ch[i] - '0';
        }
        if (!flag) {
            printf("0\n"); continue;
        }
        printf("%I64d\n", solve());
    }
    return 0;
}
View Code

J.最大公约数

严格来说做这题时这根本不是赛时状态。。。这题的题解有人放在async结果一不小心看到题解的一部分

赛时还想着把数列切成3部分:以模结果为0为边界,然后就把自己搞死了

其实此题同j不同i的数之间的差是循环节来的,就是说可以只切成2部分:前面每c[i][j]个数一组求和,用等差数列的方法算,后面n%c[i][j]个直接算,不用优化成o(1)

组内公差不好处理,转组外公差就好解决,毕竟公差正好都是凑够不会被向下取整的整数,还相等

时间复杂度o(t*m^2*log2n) c是辗转相除法次数,不超log2n

#include <bits/stdc++.h>
//#include<iostream>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
const db eps=1e-6;
const int N=700;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define CLR(a, b) memset(a, b, sizeof(a))

int gcd[N][N],c[N][N],f[N][N];
int tmp[N];
void calc(int a,int b)
{
    int pc=0,x=a,y=b;
    while(y>0)
    {
        pc++;
        int t=x%y;
        x=y;
        y=t;
    }
    gcd[a][b]=x;
    c[a][b]=pc;
    f[a][b]=pc*x*x;
}
int main()
{
    int t,i,j,n,m,p;
    memset(f,0,sizeof(f));
    for(i=1;i<700;i++)for(j=1;j<700;j++)calc(i,j);
    scanf("%d",&t);
    for(int h=0;h<t;h++)
    {
        scanf("%d%d%d",&n,&m,&p);
        ll ans=0;
        for(j=1;j<=m;j++)
        {
            for(i=1;i<=j;i++)
            {
                ll a1=0;
                for(int l=0;l<c[i][j];l++)//前c[i][j]个作为等差数列的第1项
                {
                    tmp[l]=(i+j*l)*j/f[i][j]%p;
                    a1=(a1+tmp[l])%p;
                }
                ll cnt=(n-i)/j;cnt++;
                ll num=cnt/c[i][j],g=gcd[i][j];
                ll d=(j/g*j/g)%p*c[i][j]%p;//不要管组内公差,组外公差才容易处理。这是每c[i][j]个组合起来的结果
                ans=(ans+num*(num-1)/2%p*d%p+num*a1%p)%p;
                ll rem=cnt%c[i][j];
                for(int k=0;k<rem;k++)ans=(ans+tmp[k]+num*j/g*j/g%p)%p;//第2部分
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2016-11-08 17:36  DGUT_FLY  阅读(361)  评论(0编辑  收藏  举报