训练联盟训练赛之 ICPC Greater New York Region 2019

这场比赛感觉,难度区分不是很明显qwq(可能是我太菜了吧)

很快签完4个题,然后写完第五个题就开始挂机写作业去了。

C实际上不难,但是比赛的时候确实没有思路,不知道怎么处理近似这种无限传递的问题。

重点讲一下C和J吧。J其实我是通过打表来做的。

先贴四个签到题的代码:

 

A.FYI

给定一个7位数,判断前三个数字是不是555.

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e9+7;
int main()
{
    fast;
    string s;
    cin>>s;
    if(s.substr(0,3)=="555") cout<<"YES\n";
    else cout<<"NO\n";
    return 0;
}
View Code

 

G • Simple Collatz Sequence 

 

给定一个这样的函数,再给定x,问你多少次操作后能把x变成1.

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e9+7;
int main()
{
    fast;
    ll n;
    cin>>n;
    int cnt=0;
    while(n!=1){
        if(n%2==1) n+=1;
        else n/=2;
        cnt++;
    } 
    cout<<cnt<<'\n';
    return 0;
}
View Code

 

H • Problematic Public Keys 

给定 n个整数,每个整数都是由两个质数相乘得到的。找出这些质数,一行五个输出。直接模拟就行。

被卡了两发PE()很难受

先把每个数分解成两个质数,然后用unique去重就好

具体做法是:

ans.erase(unique(ans.begin(),ans.end()),ans.end());

这里的ans是你定义的vector。下面是完整代码:

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e9+7;
vector<int>ans;
bool isprime(int a)
{
    for(int i=2;i<=sqrt(a);i++){
        if(a%i==0) return false;
    }
    return true;
}

void getnum(int a){
    for(int i=2;i<=sqrt(a);i++){
        if(isprime(i)&&isprime(a/i)&&a%i==0){
            ans.pb(i);
            ans.pb(a/i);
            break;
        }
    }
}

int main()
{
    fast;
    int m;
    cin>>m;
    while(m--){
        int n;
        cin>>n;
        getnum(n);
    }
    sort(ans.begin(),ans.end());
    ans.erase(unique(ans.begin(),ans.end()),ans.end());
    int len=ans.size();
    int cnt=0;
    for(int i=0;i<len;i++){
        cout<<ans[i];
        cnt++;
        if(i==len-1) break;
        if(cnt%5==0) cout<<'\n';
        else cout<<' ';
    }
    return 0;
}
View Code

 

K • Regional Team Names

给你一个队名,要求队名必须由“-”来分隔,“-”前面长度在(1,8]之间,“-”后面长度在[1,24]之间。“-”后面再出现的“-”按照一个普通字符算,空格也看成一个字符。

直接处理就好。我还SB的WA了一发。

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e9+7;
int main()
{
    fast;
    string s;
    getline(cin,s);
    int len=s.length();
    int pos=0;
    for(int i=0;i<len;i++){
        if(s[i]=='-') {
            pos=i;
            break;
        }
    }
    bool flag=true;
    if(pos<=1||pos>8) flag=false;
    if(len-pos<=1||len-pos>24) flag=false;
    if(!flag) cout<<"NO\n";
    else cout<<"YES\n"; 
    return 0;
}
View Code

 

下面就到了我们关键的两道题了:

J • How Many Unicycles in a Wheel? 

给你一个n个点的完全图。

现在把这张完全图改一改。先生成一个它的生成树,再添加一条边,要求图中只可以有一个环。外围的点还不能被绕开,我们定义这种环叫P环。

求把n个点的完全图改成P环的方案数。

这个题说实话,我是打表来做的。

我们从n=3开始看。

n=3的时候我们可以从6条边任意删除2条边。答案是C(6,3)= 15 种

n=4的时候,先从8条边里面任意删除3条边,但是发现,当删除一组邻边+邻边夹着的对角边是不符合条件的删法。

那么答案是C(8,3)- 4 = 52 种

n=5,样例给出来,170种

n=6的时候,先从12条边里面任意删除5条边。这种情况是C(12,5)= 792 种

然后我们考虑不符合条件的删法。

我们设与中心点相连的边为A类边,否则就是B类边

那么我们就有六种组合需要考虑了,分别是(0,5),(1,4),...(5,0)。

显然(0,5) (5,0)是一定满足条件的。

(1,4)和(4,1)也是满足条件的。(拿1,4来举例,删外面4条边里面一条边导致还有两个环的话,一定会有外围的孤立点存在,这是不符合生成树定义的)

(3,2)其实也是不可以的(没找到反例。。具体我不知道怎么去证明)

(2,3)是可以构造出来的。我们结合这个图一起看

我们先任意在里面取2条,在外面取3条,取法种数是C(6,2)*C(6,3) = 300 种。

下面我们来计算(2,3)情况下可以满足条件的取法:

如果里面删两条邻边,外面只可以删对面另一侧的三条边。删两条邻边有6种删法(旋转):种类数6 * 1 = 6 种 (例如:删除了边7和8,外面只能删除3,4,5来满足条件了)

如果里面删两条对边,显然现在有四个环,那么在外面只需要把四个环破坏掉三个即可。种类数6 * 4 = 24 种(例如,删除了边7和10,外面可以在1,(2,3),4,(5,6)四组中选择三组删掉一条)

否则,例如删除了边7和9,仍然有四个环,那么需要在(1,6),(2,3),4,5四组中选择三组删掉一条,但这种情况你会发现,在之后的构造里面有重复的情况出现。所以种类是是6 * 4 / 2 = 12 种

所以n=6的时候总数是 792 - 300 + 6 + 24 + 12 = 534 种。

然后我们发现,对于n个点的完全图,种类数一定是n的倍数。我们还惊奇的发现:

15/3=5

52/4=13

170/5=34

534/6=89

这些好像是斐波那契数列里面的项

然后我们就瞎猫碰上死耗子一样得到了答案Ans(n)=n*F(2n-1)

那就很简单了:

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e5+7;
ll f[10000];
int main()
{
    fast;
    int n;
    cin>>n;
    f[1]=1,f[2]=1;
    for(int i=3;i<8200;i++) f[i]=(f[i-1]+f[i-2])%mod;
    cout<<n*f[2*n-1]%mod<<'\n';
    return 0;
}
View Code

 

这里还有一个正解,来自南理工的官方题解:

对于一个这样的图,设外圈点数为n,Unicycles的数量为n*S(n)

S(n)为不同的图的数目,乘n是旋转后的结果

对S(n+1),相当于在外圈多加了一个点,
对于圈的形成,可分为三种情况:
1.只有中心点不在圈上
2.k个点在圈上,中心点也在圈上,k<=n 设M(i)为第二种情况下,有i个点不在圈上的图的数目

显然M(0)=1,M(1)=3

我们在圈的旁边添加一个新点
这个点有三种添加方式:连接中心点,连接圈上的点(外圈),连接圈以外的点
每种方式分别是M(i)种图 其中第二三种方式有重复,即存在这个点同时与两边相连的情况,即为M(i-1)
综上,M(i+1)=3*M(i)-M(i-1)。 S(n)=sum(1,M(0),…,M(n-2));

当然可以用矩阵快速幂去处理,不过暴力的做法也是可以的。

 

C • Pass the Buck

最后一题了。

给定n个人构成的一张图,x手里有一个球,他可以把球给和他相邻的人(也可以给他自己),给每个人的概率是等可能的。

当一个人把球给自己的时候,游戏结束,这个人获胜。

求从x出发,最终y获胜的概率是多少(保留五位小数)。

这里我当时没想到,但实际上这是数据结构课上就学过的东西。

设一个邻接矩阵G,那么G[i][j]就表示从i到j长度为1的路径数

那G2[i][j]就表示从i到j长度为2的路径数

以此类推,Gn[i][j]就是长度为n的路径数

那我们这一题就抽象一下,用G[i][j]表示从i到j的可能性,设i和x个人相连接,那么传给每个人(包括自己)的概率就是1 / (x+1) 。换句话说就是 G[i][j] = 1 / (degree(i) + 1).

自己直接传给自己实际上就是G0[i][j] 了。(当然,自己传给自己那一定有i=j的)

那最后的答案就是:

还要除以degree(j)+1的原因就是,最后j还要自己传给自己,概率是1/(degree(j)+1)

 

由于我们只需要保留5位小数

直接设无穷在1005就好 精度已经够了

由于都是矩阵乘法,建议使用矩阵快速幂,不然显然会超时。

这里直接定义了矩阵类,可以直接用这个模板。

代码里面 下标都-1了是因为,我们使用的时候下标是从0开始的,但是题目的输入是从1开始的,统一一下。

#include<bits/stdc++.h>
using namespace std;
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
bool ispow(ll n){return (n&(n-1))==0;}
#define lowbit(x) (x&(-x))
const int mod=1e9+7;
int num[25];
class matrix{
    public:
        matrix(int nn,int mm){
            n=nn;
            m=mm;
        }
        ~matrix(){};
        int n,m;
        double val[21][21];
        void init(){
            mem(val,0);
        }
        
        matrix operator *(const matrix b) const{
            matrix c(n,b.m);
            c.init();
            for(int i=0;i<n;i++)
                for(int j=0;j<m;j++)
                    for(int k=0;k<b.m;k++)
                        c.val[i][k]=c.val[i][k]+(val[i][j]*b.val[j][k]);
            return c;
        }
        
        matrix operator +(const matrix b) const{
            matrix c(n,b.m);
            c.init();
            for(int i=0;i<n;i++)
                for(int j=0;j<m;j++) c.val[i][j]=val[i][j]+b.val[i][j];
            return c;
        }
};

matrix qpow(matrix a,int n){
    matrix res(a.n,a.m);
    res.init();
    for(int i=0;i<a.n;i++) res.val[i][i]=1;
    while(n){
        if(n&1) res=res*a;
        a=a*a;
        n/=2;
    }
    return res;
}

int main()
{
    int n,p;
    scanf("%d %d",&n,&p);
    matrix g(n,n),ans(n,n);
    g.init();
    ans.init();
    for(int i=0;i<n;i++){
        scanf("%d",&num[i]);
        for(int j=0;j<num[i];j++){
            int r;
            scanf("%d",&r);
            g.val[i][r-1]=1.0/(num[i]+1);
        }
    }
    for(int i=0;i<=1005;i++) ans=ans+qpow(g,i);
    for(int i=0;i<p;i++){
        int t,st,ed;
        scanf("%d %d %d",&t,&st,&ed);
        db anss=ans.val[st-1][ed-1]*(1.0/(num[ed-1]+1));
        printf("%d %.5f\n",t,anss);
    }
    return 0;
}
View Code

那题解就到这里了,据说I也是个可做的题,之后有时间会补的。

之后还打算补一补CF

早睡的flag又倒了(

posted @ 2020-04-29 00:52  Doomfist  阅读(332)  评论(0编辑  收藏  举报