CCSU團隊訓練賽 ( A 數學 B tarjan F dij G dp H 線段樹 )

題目鏈接

設的虛擬,可能進不去,每個題有給原題鏈接。算作是私人題解吧。

A - Play the Dice HDU - 4586 

There is a dice with n sides, which are numbered from 1,2,...,n and have the equal possibility to show up when one rolls a dice. Each side has an integer ai on it. Now here is a game that you can roll this dice once, if the i-th side is up, you will get ai yuan. What's more, some sids of this dice are colored with a special different color. If you turn this side up, you will get once more chance to roll the dice. When you roll the dice for the second time, you still have the opportunity to win money and rolling chance. Now you need to calculate the expectations of money that we get after playing the game once.

 

題意:n面的色子,m面特殊面,進行一次擲色子,概率期望爲p  第二次 m/n*p  第三次 m/n*m/n*p 。。。。求和就是等比求和公式

無限循環了怎麼辦。取極限 等比求和公式 S=a1*\frac{(1-q^n)}{1-q}

a1=m/n

分三種情況討論:

1、當a1爲0時,輸出0.00;

2、當a1等於1時,說明可以無限的投擲下去,輸出inf;

3、當a1< 1時,N無窮大時,1-q^N  的q^N 無限接近於0,那麼原式變爲a1/(1-q)。

#include<bits/stdc++.h>
using namespace std;
const int N=2e2+10; 
int n,m,vis[N],a[N];
int sum,cnt;
double p,q;
const double eps=1e-6;
int main()
{
	while(~scanf("%d",&n))
	{
		sum = 0,cnt = 0;
		memset(vis,0,sizeof(vis));
		for(int i = 1; i <= n; i++)
		{
			scanf("%d",&a[i]);
			sum += a[i];
		}
		p = (sum*1.0)/n;
 
		scanf("%d",&m);
		int x;
		for(int i = 1; i <= m; i++)
		{
			cin >> x;
			if(vis[x]) continue;
			vis[x] = 1;
			cnt++;
		}
		q = (1.0*cnt)/n;
 
		if(fabs(p) < eps) puts("0.00");
		else if(fabs(q-1) < eps) puts("inf");
		else printf("%.2lf\n",p/(1-q));
	}
	return 0;
}

B - TWO NODES HDU - 4587

Suppose that G is an undirected graph, and the value of stab is defined as follows:


Among the expression,G -i, -j is the remainder after removing node i, node j and all edges that are directly relevant to the previous two nodes. cntCompent is the number of connected components of X independently.
Thus, given a certain undirected graph G, you are supposed to calculating the value of stab.

題意:去除兩個點 使得剩下的圖 聯通塊的個數 最大,

做法:百度有個人的博客刷屏了  他寫的題意是強聯通塊的個數,如果是強連通塊的話 下面這個樣例答案應該是4  而他的代碼是3

因爲兩個點只有一條線相連,那麼這兩個點屬於不同的強連通

如果只是去除一個點的話 好做,用tarjan 計算一下 去除這個點時附近產生的 連通塊的個數 即可

如果是去除兩個點,枚舉去除的那個點,然後再套用tarjan。

代碼就懶的打了,貼的別人的

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
const int N=5010;
struct data
{
    int to,next;
} tu[N*N];
int head[N],low[N],dfn[N];///dfn[]記錄某個點被訪問到的次數,low[]記錄某個點或其子樹回邊的最小步數的點
int cut[N];///是否爲割點且該點連接邊的數量
int ip;
int step,rt_son,scc_cnt;
void init()
{
    ip=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v)
{
    tu[ip].to=v,tu[ip].next=head[u],head[u]=ip++;
}
void tarjan(int u,int del)
{
    //cout<<u<<' ';
    dfn[u] = low[u] = step++;
    for(int i = head[u]; i!=-1 ; i=tu[i].next)
    {
        int to = tu[i].to;
        if(to==del)continue;
        if(!dfn[to])///表示未被訪問的點
        {
            tarjan(to,del);
            low[u]=min(low[u],low[to]);
            if(low[to]>=dfn[u])
                cut[u]++;
        }
        else
            low[u]=min(low[u],dfn[to]);
    }
}
void solve(int n)
{
    int ans=0;
    for(int j=1; j<=n; j++)
    {
        memset(dfn, 0, sizeof(dfn));
        for(int i=1; i<=n; i++)
            cut[i]=1;
        step = 1;
        int sum=0;
        for (int i = 1; i <=n; i++)
            if (i!=j&&!dfn[i])
            {
                sum++;//聯通塊的個數 
                cut[i]=0;
                tarjan(i,j);
            }
        for(int i=1; i<=n; i++)
            if(i!=j)
                {
                	//if(sum+cut[i]-1)
                	ans=max(ans,sum+cut[i]-1);
				}
    }
    printf("%d\n",ans);
}
int main()
{
    int m,n;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            a++,b++;
            add(a,b);
            add(b,a);
        }
        solve(n);
    }
    return 0;
}
/*

10 12
0 1
1 2
2 3
3 1
0 4
4 5
5 6
6 4
0 7
7 8
8 9
9 7



 


*/ 

F - Shortest Subchain  URAL - 1651 

題意:給你一條單鏈,求此單鏈上 從起點出發到達終點最短的一條子鏈,意思就是說可能會有環,那麼這個環去掉即可。

做法:對於 這條鏈上 前後出現了相同的點時 連一條長度爲0 的邊,其餘的按照鏈上的順序連權值爲1的邊,跑一次最短路,接着輸出最短路就好了。很坑的是,題意沒告訴你是多組輸入,結果我單組輸入 wa到炸

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e5+10;
vector<pair<int,int> >G[N];
int n,a[N],vis[10010];
int dis[N],fa[N],ans[N];
struct node
{
    int u,w;
    bool operator <(const node &o) const
    {
        return w>o.w;
    }
};
void bfs()
{
    priority_queue<node>que;
    rep(i,1,n) dis[i]=1e9;
    dis[1]=0;
    que.push({1,dis[1]});

    while(que.size()){
        node now=que.top();que.pop();
        for(auto it:G[now.u]){
            if(dis[it.first]>dis[now.u]+it.second){
                dis[it.first]=dis[now.u]+it.second;
                fa[it.first]=now.u;
                que.push({it.first,dis[it.first]});
            }
        }
    }
}


int main()
{
    while(~scanf("%d",&n))
    {

       rep(i,1,n) G[i].clear(),fa[i]=0;
       memset(vis,0,sizeof(vis));

        rep(i,1,n) a[i]=read();

        vis[a[1]]=1;
        for(int i=2;i<=n;++i){
            if(vis[a[i]]!=0) G[vis[a[i]]].push_back({i,0});
            vis[a[i]]=i;
            G[i-1].push_back({i,1});
        }

        bfs();
        //rep(i,1,n) printf("i:%d dis:%d\n",i,dis[i]);

        //int len=0;


        int now=n;
        stack<int>sta;
        while(now!=1){sta.push(now);now=fa[now];}
        sta.push(1);


        int pre=0;
        while(sta.size())
        {
            if(a[sta.top()]!=pre) printf("%d ",a[sta.top()]);
            pre=a[sta.top()];
            sta.pop();
        }
        puts("");
    }
}
/*
9
1 2 7 3 2 8 4 8 5

10
1 2 3 4 5 2 4 6 7 5
1 2 3 4 5
7
1 2 3 1 4 2 5

1 2 5
*/

 

G - Sum of Digits URAL - 1658 

Petka thought of a positive integer n and reported to Chapaev the sum of its digits and the sum of its squared digits. Chapaev scratched his head and said: “Well, Petka, I won't find just your number, but I can find the smallest fitting number.” Can you do the same?

題意:構造n位   每位和爲s1  每位的平方和爲2的最小的數。

做法:簡單dp:dp[i][s1][s2] 第i爲爲dp[i][s1][s2]  和爲s1  平方和爲s2   。內存爆炸,簡化一下,發現每個狀態的dp 是唯一的。

於是去掉一維 dp[s1][s2]  用ans[s1][s2]保存到達s1、s2 需要填的數(1~9)

t組,預處理一下,然後逆着輸出路徑即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
int dp[901][8101],ans[901][8101];
void init()
{
    for(int i=0;i<=900;++i){
        for(int j=0;j<=8100;++j) dp[i][j]=inf;
    }
    dp[0][0]=0;
    for(int i=1;i<=900;++i){
        for(int j=1;j<=8100&&j<=i*i;++j){
            for(int k=1;k<=9;++k){
                if(i-k<0||j-k*k<0) break;
                if(i-k>j-k*k) break;
                if(dp[i][j]>dp[i-k][j-k*k]+1){
                    dp[i][j]=dp[i-k][j-k*k]+1;
                    ans[i][j]=k;
                }
            }
        }
    }
}
int main()
{
    init();
    int _=read();while(_--)
    {
        int n=read(),m=read();

        if(n>900||m>8100){
            puts("No solution");
            continue;
        }
        if(dp[n][m]>100){
            puts("No solution");
            continue;
        }

        vector<int>res;

        while(n&&m){


            int now=ans[n][m];
            //printf("n:%d m:%d now:%d\n",n,m,now);
            res.push_back(ans[n][m]);
            n-=now,m-=now*now;
        }
        for(int v:res) printf("%d",v);
        puts("");


    }
}
/*
4
9 81
12 9
6 10
7 9
*/

H - Billionaires  URAL - 1650

題意:給定n個富豪的身價,以及最初始 所在的城市,然後m個富豪外出旅遊的計劃表。問每個城市 富豪的身價之和嚴格大於其他城市 時的天數。

做法:怎麼URAL的題都這樣,上一個題沒講多組輸入,這個題又搞隱性題意,兩個城市的身價之和 相同時 不貢獻答案。

被這個不貢獻答案卡了一個 小時。

讓我想起了高中英語題 

題面:His mother was a nurse

題目:Is his mother a nurse?

答案是錯的,因爲題面 用的是過去式,題面問的現在時。這題 我們班英語130分的學霸還做錯了。。。真的就是英語不好很喫虧(母語爲英語的國家真好)。

複雜模擬,幾個map 保存 富豪對應的身價,城市對應身價和  保存最大身價和的個數。線段樹搞搞就好了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e5+10;
int n,m,cnt,q;
map<string,string>name_city;
string tid[N];
map<string,int>vis;
set<string>st;
map<string,ll>city_num,name_num,ans;
map<ll,ll>num;

ll x;
struct node
{
    string s;
    ll num;
};

int t[N];
string name[N],city[N];
struct tree
{
    string city;
    ll x;
}mx[4*N];
tree Max(tree a,tree b)
{
    if(a.x>b.x) return a;
    return b;
}
void build(int id,int l,int r)
{
    if(l==r){
        mx[id].city=tid[l];
        mx[id].x=city_num[tid[l]];
        num[mx[id].x]++;
        return ;
    }
    int mid=l+r>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    mx[id]=Max(mx[id<<1],mx[id<<1|1]);
}

void up(int id,int l,int r,int pos,ll val)
{
    if(l==r){
        num[mx[id].x]--;
        mx[id].x+=val;
        num[mx[id].x]++;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos,val);
    else up(id<<1|1,mid+1,r,pos,val);
    mx[id]=Max(mx[id<<1],mx[id<<1|1]);
}

bool cmp(node a,node b)
{
    return a.s<b.s;
}

int main()
{

    n=read();
    rep(i,1,n)
    {
        string s1,s2;
        cin>>s1>>s2>>x;
        name_city[s1]=s2;
        name_num[s1]=x;
        st.insert(s2);

        city_num[s2]+=x;
    }
    m=read(),q=read();
    rep(i,1,q)
    {
        t[i]=read();
        cin>>name[i]>>city[i];
        st.insert(city[i]);
    }

    cnt=st.size();
    for(int i=1;i<=cnt;++i) {
        tid[i]=*st.begin();
        vis[*st.begin()]=i;
        st.erase(st.begin());
    }

    build(1,1,cnt);
    tree tmp=mx[1];


    int pre=0;
    rep(i,1,q)
    {
        tree tmp=mx[1];
        if(num[tmp.x]==1) ans[tmp.city]+=t[i]-pre;

        ll val=name_num[name[i]];
        up(1,1,cnt,vis[name_city[name[i]]],-val);

        name_city[name[i]]=city[i];
        up(1,1,cnt,vis[city[i]],val);
        pre=t[i];
    }
    if(m>=pre){
        tree tmp=mx[1];
        if(num[tmp.x]==1) ans[tmp.city]+=m-pre;
    }


    for(auto now:ans) if(now.second)cout<<now.first<<" "<<now.second<<endl;



}
/*
5
Abramovich London 15000000000
Deripaska Moscow 10000000000
Potanin Moscow 5000000000
Berezovsky London 2500000000
Khodorkovsky Chita 1000000000
25 9
1 Abramovich Anadyr
5 Potanin Courchevel
10 Abramovich Moscow
11 Abramovich London
11 Deripaska StPetersburg
15 Potanin Norilsk
20 Berezovsky Tbilisi
21 Potanin StPetersburg
22 Berezovsky London
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章