zoukankan      html  css  js  c++  java
  • Codeforces 802 补题

    codeforces802 A-O Helvetic Coding Contest 2017 online mirror 

    A  Heidi and Library (easy)

    水题 同B

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    using namespace std;
    const int maxn=1000000;
    int n,k,a[maxn],num;
    bool ex[maxn],need[maxn];
    int main()
    {//freopen("t.txt","r",stdin);
     scanf("%d%d",&n,&k);
     num=0;
     memset(ex,0,sizeof(ex));
     for(int i=0;i<n;i++)
     	scanf("%d",&a[i]);
     int ans=0;
     for(int i=0;i<n;i++)
     	{
         if(ex[a[i]]) continue;	
         if(num<k)
         	{
         	 num++;
         	 ans++;
         	 ex[a[i]]=true;
    		}
    		else
    			{
    			 int sum=0;
    			 memset(need,0,sizeof(need));
    			 for(int j=i+1;j<n&&sum<k-1;j++)
    			 	{
    			 	 if(!ex[a[j]])continue;
    			 	 
    			 	 if(need[a[j]]==false)sum++;
    			 	 need[a[j]]=true;
    				}
    			 for(int j=1;j<=n;j++)
    			 	{
    			 	 if(ex[j]&&(!need[j])){ex[j]=false;ex[a[i]]=true;ans++;break;}
    				}
    			}
     	}
     printf("%d
    ",ans);
     return 0;
    }
    

      

    B  Heidi and Library (medium)

    经典的内存管理OPT算法

    不过基本没有实际应用价值,因为操作系统不可能知道之后要调用哪些内存。

    用map或者堆都可以实现 堆会快很多

    map版本

    #include <bits/stdc++.h>
    using namespace std;
    set<int> s;
    int n,cs,k,c[400400],ne[400400],la[400400],cc;
    
    int main(){
    	scanf("%d %d",&n,&k);
    	for(int i=0;i<n;i++)scanf("%d",&c[i]);
    	for(int i=n-1;i>=0;i--){
    		if(!la[c[i]])ne[i]=1e6,la[c[i]]=i;
    		else ne[i]=la[c[i]],la[c[i]]=i;
    	}
    	for(int i=0;i<n;i++){
    		if(s.count(i)){s.erase(i),s.insert(ne[i]);continue;}
    		if((int)s.size()<k)
    			s.insert(ne[i]),cc++;
    		else 
    			s.erase(--s.end()),s.insert(ne[i]),cc++;
    	}
    	printf("%d
    ",cc);
    }
    

     优先队列版本

      

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<stack>
    #include<deque>
    #include<queue>
    using namespace std;
    const int maxn=500000;
    int n,k,a[maxn],num,ne[maxn],ls[maxn];
    bool ex[maxn];
    priority_queue<int>que;
    int main()
    {//freopen("t.txt","r",stdin);
     while(!que.empty())que.pop();
     scanf("%d%d",&n,&k);
     num=0;
     memset(ex,0,sizeof(ex));
     for(int i=0;i<n;i++)scanf("%d",&a[i]);
     
     for(int i=n-1;i>=0;i--)
     	{
     	 if(!ls[a[i]])ls[a[i]]=1e+8;
     	 ne[i]=ls[a[i]];
     	 ls[a[i]]=i;
    	}
     int ans=0;
     int j=1;
     int maxv=0,max2v=0;
     for(int i=0;i<n;i++)
     	{
         if(ex[i])
    	 
    	 {
    	 while(que.size()>0&&que.top()<=i)que.pop();
    	 ex[i]=false;
    	 if(ne[i]<=n)ex[ne[i]]=true;
    	 que.push(ne[i]);
    	 continue;	
    	}
         if(num<k)
         	{
         	 num++;
         	 ans++;
         	 if(ne[i]<=n)ex[ne[i]]=true;
         	 que.push(ne[i]);
    		}
    		else
    			{
    			 int nowv=que.top();
    			 que.pop();
    			 ans++;
    			 if(nowv<=n)ex[nowv]=false;
    			 if(ne[i]<=n)ex[ne[i]]=true;
    			 que.push(ne[i]);
    			}
     	}
     printf("%d
    ",ans);
     return 0;
    }
    

      

    Heidi and Library (hard)

    费用流

    考虑最暴力的方法,每次调用新的书都直接购买,这多半不是最优解。

    有没有办法优化到最优解呢?

    考虑对于书架上的每一个位置,让它在恰当的时候继续持有书,在恰当的时候购买新的书,这样我们就能找到最优解了。

    对于相同的书,连一条费用为-c[]的边(持有即相当于不用买新的所以答案-c[]),不同的书连一条费用为0的边.

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    
    #define N 200020
    
    const LL INF = 1e9;
    
    int nxt[N], cost[N], cap[N], to[N], head[N], cnt;
    
    void init(){
    	memset(head, -1, sizeof head);
    }
    
    void add_Edge(int S, int T, int c, int w){
    	nxt[cnt] = head[S], to[cnt] = T, cap[cnt] = c, cost[cnt] = w, head[S] = cnt ++;
    	nxt[cnt] = head[T], to[cnt] = S, cap[cnt] = 0, cost[cnt] = -w, head[T] = cnt ++;
    }
    
    int prv[N], vis[N];
    LL dist[N];
    
    LL SPFA(int S, int T, int vet){
    	queue <int> Q;
    	fill(dist, dist + vet, INF);
    	fill(prv, prv + vet, -1);
    	dist[S] = 0, Q.push(S), vis[S] = true;
    	while(!Q.empty() ){
    		int x = Q.front();
    		Q.pop(), vis[x] = false;
    		for(int id = head[x]; ~id; id = nxt[id]) if( cap[id] ){
    			int y = to[id];
    			if(dist[y] > dist[x] + cost[id]){
    				dist[y] = dist[x] + cost[id];
    				prv[y] = id;
    				if(!vis[y]) Q.push(y), vis[y] = true;
    			}
    		}
    	}
    
    	if(!~prv[T]){ return INF; }
    
    	int cur = T;
    	while( cur != S ) {
    		cur = prv[cur];
    		cap[cur] --;
    		cap[cur xor 1] ++;
    		cur = to[cur xor 1];
    	}
    	return dist[T];
    }
    
    int a[N], c[N], n, m;
    
    int main(){
    	//freopen("t.txt", "r", stdin);
    
    	scanf("%d %d", &n, &m);
    	for(int i = 1; i <= n; i ++) scanf("%d", a + i);
    	for(int i = 1; i <= n; i ++) scanf("%d", c + i);
    
    	init();
    
    	LL ans = 0;
    	int S = n + 1, T = 2 * n + 2;
    	for(int i = 1; i <= n; i ++){
    		ans += c[a[i]];
    		add_Edge(S, i, 1, 0);
    		add_Edge(i, S + i, 1, -INF);
    		for(int j = i + 1; j <= n; j ++){
    			if(a[i] == a[j]) add_Edge(i + S, j, 1, -c[a[j]]);
    			else add_Edge(i + S, j, 1, 0);
    		}
    		add_Edge(i + S, T, 1, 0);
    	}
    
    	for(int step = 1; step <= m; step ++){
    		LL tmp = SPFA(S, T, T + 1);
    		if(tmp >= 0) break;
    		ans += tmp;
    	}
    	cout << ans + INF * n << endl;
    }
    

      

    Marmots (easy)

    根据泊松分布的特点,对称轴两边的概率密度最大。

    用这个特点来判断是泊松分布还是平均分布。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std; 
    int a[250],b[250];
    int main()
    {
     int T;
     scanf("%d",&T);
     while(T--)
     	{
     	 
     	 for(int i=0;i<250;i++)scanf("%d",&a[i]);
     	 //for(int i=0;i<250;i++)scanf("%d",&b[i]);
     	 int mina=a[0],maxa=a[0],minb=b[0],maxb=b[0];
     	 double mida=0;
     	 for(int i=0;i<250;i++)
     	 	{
     	 	 mida+=a[i];
     	 	 //minb=min(minb,b[i]);maxb=max(maxb,b[i]);
    		}
    	 mida/=250.;
    	 int sum=0,sumb=0;
    	 double len=mida/2;
    	 for(int i=0;i<250;i++)
    	 	{
    	 		if(a[i]>(mida-len)&&a[i]<(mida+len))sum++;
    	 		if(a[i]<=1)sumb++;
    		}
    	 if(sum<180||sumb>3)printf("uniform
    ");
    	 	else printf("poisson
    ");
    	}
     return 0;
    }
    

      

    Marmots (medium)

    在D的基础上,首先判断是 泊松分布还是平均分布

    如果是泊松分布求所有值的平均值,否则求最大值和最小值的平均值。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int a[250],b[250];
    int main()
    {
     int T;
     scanf("%d",&T);
     while(T--)
        {
         for(int i=0;i<250;i++)scanf("%d",&a[i]);
         //for(int i=0;i<250;i++)scanf("%d",&b[i]);
         double mina=a[0],maxa=a[0],minb=b[0],maxb=b[0];
         double mida=0;
         for(int i=0;i<250;i++)
            {
             mida+=a[i];
             mina=min(mina,(double)a[i]);maxa=max(maxa,(double)a[i]);
            }
         mida/=250.;
         int sum=0,sumb=0;
         double len=mida/2;
         for(int i=0;i<250;i++)
            {
                if(a[i]>(mida-len)&&a[i]<(mida+len))sum++;
                if(a[i]<=1)sumb++;
            }
         if(sum<180||sumb>3)//printf("uniform
    ");
         	{
         	 printf("%.0lf
    ",(mina+maxa)/2+0.5);
    		}
            else //printf("poisson
    ");
            	{
            	 printf("%.0lf
    ",mida+0.5);
    			}
        }
     return 0;
    }
    

      

    Marmots (hard)

    由于出现了负数,所以D中简单粗暴的方法不可取了。

    不过数据并没有变复杂,由于平均分布相对于泊松分布更加离散,所以方差会有明显的区别。

    利用方差来判断,就不怕负数了。

    #include<bits/stdc++.h>
    #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 int read() {
    	int 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^48); ch=getchar();}
    	return x*f;
    }
    
    const int N = 251;
    const int P = 1005;
    const double e = 2.718281828459045235360287471352;
    
    int a[N];
    double tp1[P<<1],tp2[P<<1];
    
    int main() {
    
    	int T=read();
    	while (T--) {
    		int mx=0;
    		double mean=0; rep(i,1,250) a[i]=read(),mean+=a[i],mx=max(mx,a[i]);
    		double D=0;	mean/=250; rep(i,1,250) D+=a[i]*a[i];
    		D/=(double)250; D-=mean*mean;
    		double sigma = sqrt(D);
    		if (mx/sigma<=1.9) puts("uniform"); else puts("poisson");
    	}	
    	
    	
    	return 0;	
    }
    

      

    Fake News (easy)

    公共子序列问题 O(N^2)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<string>
    #include<vector>
    #include<cstring>
    using namespace std;
    int dp[1000][1000];
    int LCS(int n1,int n2,string s1,string s2)
    {
     for(int i=0;i<n1;i++)
     	for(int j=0;j<n2;j++)
     		{
     		 if(i>0)dp[i][j]=dp[i-1][j];
    		 if(j>0&&dp[i][j-1]>dp[i][j])dp[i][j]=dp[i][j-1];
    		 if(s1[i]==s2[j])
    		 	{
    		 	 if(i==0||j==0)dp[i][j]=1;
    		 	 else dp[i][j]=dp[i-1][j-1]+1;	
    			}
    		}
     return dp[n1-1][n2-1];
    }
    int main()
    {//freopen("t.txt","r",stdin);
     ios::sync_with_stdio(false);
     string s1,s2;
     s2="heidi";
     while(cin>>s1)
     	{
     	 memset(dp,0,sizeof(dp));
     	 int len=LCS(s1.length(),s2.length(),s1,s2);
     	 if(len==5)printf("YES
    ");
     	 	else printf("NO
    ");
    	 s1.clear();s2.clear();	
    	}
     return 0;
    }
    

      

    Fake News (medium)

    很简单的一道计数题 直接看代码吧~

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int Comb[110][6], C[110], S;
    int main(){
        int i, j;
        for(i=0;i<=45;i++){
            Comb[i][0]=1;
            for(j=1;j<=5&&j<=i;j++)Comb[i][j]=Comb[i-1][j]+Comb[i-1][j-1];
        }
        scanf("%d",&S);
        for(i=45;i>=5;i--){
            while(S>=Comb[i][5]){
                C[i]++;
                S-=Comb[i][5];
            }
        }
        for(i=0;i<45;i++){
            while(C[i]--)printf("b");
            printf("a");
        }
        printf(" aaaaab
    ");
    }
    

      

    Fake News (hard)

    后缀数组+记忆化搜索

    很有趣的一道后缀数组题

    题目的意思就是让我们求一个字符串L所有子串在这个串中出现的次数。

    这种问题用脚指头想都知道肯定要上后缀数组啦!

    对于数组height[l...r] 其中的最小值就是他们的公共前缀的长度,而这个公共前缀就是L的一个唯一的子串。r-l+1就是它出现的次数。(ps:不懂height数组的自行学习后缀数组再来看)

    怎样高效统计呢?

    对于height[0,len(L)-1]我们能否高效的找到它的最小值?可以。

    那么假设最小值的位置是mid 然后我们把height分成l..mid mid+1....r分别计算,那么怎么合并呢?

    两个子片段的公共最小Height值就是他们的公共前缀长度,我们可以知道这个公共前缀出现的次数,但是同时要排除他们在字串中贡献的值。

    太难描述了,具体转移方法看代码吧。

    注意,mid一定是最小值所在的位置,不可以随意划分height数组,那样是错的。

    求mid不能太暴力,会TLE。

    #include <iostream>  
    #include <cstring>  
    #include <cstdio>  
    #include<vector>
    using namespace std;  
      
    const int MAX = 100500;  
    const int nMAX = 105;  
    const int mMAX = 1005;  
      
    int strnum;  
    char str[MAX]; 
    int source[MAX];  
    int sa[MAX], rk[MAX], height[MAX];  
    int wa[MAX], wb[MAX], wv[MAX], wd[MAX];  
    bool vis[nMAX];  
    int id[MAX];  
    int anslen, anspos[mMAX], ansnum;  
      const int MAXN=200000+100;
    void radix(int *str,int *a,int *b,int n,int m)
    {
     static int count[MAXN];
     memset(count,0,sizeof(count));
     for(int i=0;i<n;++i)++count[str[a[i]]];
     for(int i=1;i<=m;++i)count[i]+=count[i-1];
     for(int i=n-1;i>=0;--i)b[--count[str[a[i]]]]=a[i];
    }
    
    void sorted_suffix_array(int *str,int *sa,int n,int m)
    {
     static int rank[MAXN],a[MAXN],b[MAXN];
     for(int i=0;i<n;++i)rank[i]=i;
     radix(str,rank,sa,n,m);
     
     rank[sa[0]]=0;
     for(int i=1;i<n;++i)rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
     for(int i=0;(1<<i) <n;++i)
     	{
     	 for(int j=0;j<n;++j)
     	 	{
     	 	 a[j]=rank[j]+1;
     	 	 b[j]=j+(1<<i)>=n? 0:rank[j+(1<<i)]+1;
     	 	 sa[j]=j;
    		}
    	 radix(b,sa,rank,n,n);
    	 radix(a,rank,sa,n,n);
    	 rank[sa[0]]=0;
    	 for(int j=1;j<n;++j)
    	 	{
    	 	 rank[sa[j]]=rank[sa[j-1]]+(a[sa[j-1]]!=a[sa[j]]||b[sa[j-1]]!=b[sa[j]]);
    		}
    	}
    }
    
    
    void calc_height(int *str,int *sa,int *h,int n)
    {
     static int Rank[MAXN];
     int k=0;
     h[0]=0;
     for(int i=0;i<n;++i)Rank[sa[i]]=i;
     for(int i=0;i<n;++i)
     	{
     	 k= k==0?0:k-1;
     	 if(Rank[i]!=0)
     	   while(str[i+k]==str[sa[Rank[i]-1]+k])++k;
     	 h[Rank[i]]=k;
    	}
    }
    int stlen;
    long long dp(long long  l,long long  r,long long &summ,vector<int>&nemi,int flag)
    {
    
     if(l==r){summ=(long long)stlen-(long long)sa[l];return summ;}
     int mid=-1;
     vector<int>nemr;
     if(flag!=-1)mid=nemi[flag]-1;
     	else
     		{
     		 nemi.push_back(l+1);
     		 for(int i=l+1;i<r;i++)
     		 	{
     		 	 if(height[i+1]<height[nemi[(int)nemi.size()-1]])nemi.push_back(i+1);
    			}
    		 flag=(int)nemi.size()-1;
     		 mid=	nemi[flag]-1;
    		}
     
     long long sum1,sum2;
     long long int minh=min(min(dp(l,mid,sum1,nemi,flag-1),dp(mid+1,r,sum2,nemr,-1)),(long long)height[mid+1]);
     summ=sum1+sum2+minh*(r-l+1)*(r-l+1)-minh*(mid-l+1)*(mid-l+1)-minh*(r-mid)*(r-mid);
     nemr.clear();
     return minh;
    }
     long long int solve(char *st)
     {
        stlen=strlen(st);
       for(int i=0;i<stlen;i++)
       	    source[i]=st[i]-'a'+1;
       sorted_suffix_array(source,sa,(int)stlen,126);
       calc_height(source,sa,height,(int)stlen);
       height[0]=1e+8;
       
      long long int ans=0;
      vector<int>mi;
      mi.clear();
      dp(0,stlen-1,ans,mi,-1);
      
      return ans;
    } 
    
       
    
    
    
    int main()
    {//freopen("t.txt","r",stdin);
     int T;
     scanf("%d",&T);
     while(T--)
     	{
     	 scanf("%s",&str);
     	 printf("%I64d
    ",solve(str));
    	}
     return 0;
    }
    

      

    Send the Fool Further! (easy)

    水题

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<string>
    #include<vector>
    #include<cstring>
    using namespace std;
    vector<int>adj[200];
    int w[200][200];
    int dist[200];
    void dfs(int cur,int fa,int len)
    {
     for(int i=0;i<adj[cur].size();i++)
     	{
     	 int ne=adj[cur][i];
     	 if(ne==fa)continue;
     	 dist[ne]=min(dist[ne],len+w[cur][ne]);
     	 dfs(ne,cur,len+w[cur][ne]);
    	}
    }
    int main()
    {//freopen("t.txt","r",stdin);
     int n;
     scanf("%d",&n);
     int u,v;
     for(int i=0;i<n-1;i++)
     	{
     	 scanf("%d%d",&u,&v);
     	 scanf("%d",&w[u][v]);
     	 w[v][u]=w[u][v];
     	 adj[u].push_back(v);
     	 adj[v].push_back(u);
    	}
     for(int i=1;i<n;i++)
     	dist[i]=99999999;
     dist[0]=0;
     dfs(0,-1,0);
     int ans=0;
     for(int i=0;i<n;i++)
     	ans=max(ans,dist[i]);
     printf("%d
    ",ans);
     return 0;
    }
    

      

    Send the Fool Further! (medium)

    比较有趣的树形DP

    考虑最优解,必有两种情况,要么在0结束,要么不在0结束。对于其他节点也是一个道理。

    所以设dp[i][bool]为从i出发,的最优解,bool为0则最后回到i否则不用回到i

    转移方程直接看程序吧 很简单。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 200000;
    int dp[maxn][2];
    vector< pair<int,int> > E[maxn];
    int n,k;
    
    bool cmp(const pair<int,int> &a, const pair<int,int> &b){
    	return (a.first > b.first);
    }
    
    void dfs(int u, int p){
    	vector< pair<int,int> > c;
    	set<int> st;
    
    	for(auto e: E[u]){
    		int v = e.first;
    		int cst = e.second;
    		if(v == p) continue;
    		dfs(v,u);
    		c.push_back({dp[v][0] + cst,v});
    	}
    
    	sort(c.begin(), c.end(), cmp);
    	int tk = min(k-1, (int)(c.size()));
    	for(int i = 0; i < tk; i++){
    		dp[u][0] += c[i].first;
    		st.insert(c[i].second);
    	}
    
    	int extra = 0;
    	if(tk != (int)(c.size()))
    		extra = c[tk].first;
    
    	for(auto e: E[u]){
    		int v = e.first;
    		int cst = e.second;
    		if(v == p) continue;
    
    		if(st.count(v) == 0)
    			dp[u][1] = max(dp[u][1], dp[v][1] + dp[u][0] + cst);
    		else
    			dp[u][1] = max(dp[u][1], dp[u][0] - dp[v][0] + extra + dp[v][1]);
    	}
    	st.clear();
    	c.clear();
    }
    
    int main(){
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i < n; i++){
    		int u,v,c;
    		scanf("%d%d%d", &u, &v, &c);
    		E[u].push_back({v,c});
    		E[v].push_back({u,c});
    	}
    	dfs(0,-1);
    	cout << max(dp[0][0], dp[0][1]) << "
    ";
    	return 0;
    }
    

      

    Send the Fool Further! (hard)

    这道题题意说的不是很清楚 总的来说是让我们求E(0)

    E(0)和E(v)有线性关系,v是0的孩子。

    所以,暴力解方程组的方法是可以求出来的。但是复杂度太高O(n^3)

    有没有聪明一点的方法呢?

    还是考虑E(0)和E(v)的关系

    E(v)由它的孩子和E(0)线性组合而成。假设E(v)不考虑0的情况下期望为G(v)G(v)可以在dfs()的过程中求出来。

    我们通过一定的代数变形可以直接由G(v)推出E(v)

    嗯。。大致就是这样。。还是那句话 近世代数太重要了。

    代码很清晰

    直观的看的话 在树上求解问题,最关键的就是找出递归关系,

    也就是对于当前节点求解(做到)不需考虑它的父亲部分(这样我们离答案就很近了因为最后的答案G【0】就是不需要考虑父亲的)。

    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <vector>
    #include <string>
    #define SIZE 100005
    #define MOD 1000000007
    
    using namespace std;
    typedef long long int ll;
    
    struct edge
    {
    	int to,cost;
    	edge(int to=0,int cost=0):to(to),cost(cost){}
    };
    vector <edge> vec[SIZE];
    ll F[SIZE],G[SIZE];
    int nd[SIZE];
    int n;
    
    ll mpow(ll m,ll t)
    {
    	if(t==0) return 1LL;
    	ll ret=mpow(m*m%MOD,t/2);
    	if(t%2==1) ret=ret*m%MOD;
    	return ret;
    }
    ll inv(ll m)
    {
    	return mpow(m,MOD-2);
    }
    void dfs(int v=0,int p=-1)
    {
    	if(vec[v].size()==1)
    	{
    		F[v]=G[v]=0;
    		return;
    	}
    	ll sumG=0,sumF=vec[v].size();
    	for(int i=0;i<vec[v].size();i++)
    	{
    		edge e=vec[v][i];
    		sumG+=e.cost;
    		if(sumG>=MOD) sumG-=MOD;
    		if(e.to!=p)
    		{
    			dfs(e.to,v);
    			sumG+=G[e.to];
    			if(sumG>=MOD) sumG-=MOD;
    			sumF-=F[e.to];
    			if(sumF<0) sumF+=MOD;
    		}
    	}
    	ll g=inv(sumF);
    	F[v]=g;
    	G[v]=g*sumG%MOD;
    }
    int main()
    {   freopen("t.txt","r",stdin);
    	scanf("%d",&n);
    	for(int i=0;i<n-1;i++)
    	{
    		int a,b,c;
    		scanf("%d %d %d",&a,&b,&c);
    		vec[a].push_back(edge(b,c));
    		vec[b].push_back(edge(a,c));
    	}
    	dfs();
    	printf("%lld
    ",G[0]);
    	return 0;
    }
    

      

    April Fools' Problem (easy)

    每个序列排序后前k个数的和

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    int num[3000];
    int main()
    {//freopen("t.txt","r",stdin);
     int n,m,t;
     int mins=-1;
     scanf("%d%d",&n,&m);
     for(int i=0;i<n;i++)
     {
     
     	scanf("%d",&num[i]);
     }
     sort(num,num+n);
      int ans=0;
     for(int i=0;i<m;i++)
     	ans+=num[i];
     printf("%d
    ",ans);
     return 0;
    }
    

    April Fools' Problem (medium)

     很有趣的一道贪心题。

    给定两个正整数序列A和B

    求两个A和B的长度为k的子序列 a和b满足 a中的第i个元素在A中的位置<= b中的第i个元素在B中的位置 并且和最小。

    用贪心的方法,先求一个最小的合法解 a[i]和b[j]且 j>=i,然后他们之间[i...j]就可以放反向的数对了也就是在找最小解的时候可以允许j<i了 。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long int LL ;
    const int maxn=3000;
    LL a[maxn],b[maxn],va[maxn],vb[maxn],verse[maxn];
    int n,k;
    int main()
    {//freopen("t.txt","r",stdin);
     scanf("%d%d",&n,&k);
     for(int i=0;i<n;i++)scanf("%I64d",&a[i]);
     for(int i=0;i<n;i++)scanf("%I64d",&b[i]);
     LL ans=0;
     b[n]=1e+18;
     while(k--)
     	{int ra=-1,rb;
     	 for(int i=n-1,j=n,minb=n;i>=0;i--)
     	 	{
     	 	 if(j>i)j=i;
     	 	 if(b[j]<b[minb]&&!vb[j])minb=j;
     	 	 
     	 	 while((j-1)>=0&&verse[j-1])if(b[--j]<b[minb]&&!vb[j])minb=j;
     	 	 if(!va[i]&&(ra==-1||a[i]+b[minb]<a[ra]+b[rb]))ra=i,rb=minb;
    		}
    	 ans+=a[ra]+b[rb];va[ra]++;vb[rb]++;
    	 for(int i=ra;i<rb;i++)verse[i]++;
    	 for(int i=rb;i<ra;i++)verse[i]--;
    	}
     printf("%I64d
    ",ans);
     return 0;
    }
    

      

    April Fools' Problem (hard)

    题意和N一样 数据提高到了500000 非常有趣的一道优化题

    显然N中我们O(n^2)的算法要TLE

    有什么办法优化到O(nlogn)么?

    1.首先考虑当前已经选定了N个题目以及他们合法的打印时间,对于以后可选的打印时间,可以用来优化答案。O(n)

    2.那么主要问题就变成了如何选取恰当的N个初始题目。

    我们用二分的方法选取一个阀值mid 比它更优的 我们才让它成为备选题目,这样不断二分mid 总会找到一个最恰当的mid使得选中的题目正好是m个最优的。

    当然如果出现比m多的情况说明他们是相等的题目和打印时间。

    看代码吧 很清晰。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    
    #define N 500050
    
    priority_queue <LL, vector <LL>, greater<LL> > Qa;
    priority_queue <LL> Qb;
    
    LL a[N], b[N];
    int n, m;
    
    const LL INF = 1e13;
    
    int main() {
    //	freopen("in.txt", "r", stdin);
    
    	scanf("%d %d", &n, &m);
    
    	for (int i = 1; i <= n; i ++) scanf("%I64d", a + i);
    	for (int i = 1; i <= n; i ++) scanf("%I64d", b + i);
    
    	LL st = 0, en = INF, ans = 0;
    
    	while (st <= en ) {
    		LL mid = (st + en) >> 1;
    		while ( !Qa.empty() ) Qa.pop();
    		while ( !Qb.empty() ) Qb.pop();
    		LL tmp = 0;
    		int sz = 0;
    		for (int i = 1; i <= n; i ++) {
    			Qa.push(a[i]);
    			LL tmp1 = Qa.top() + b[i] - mid;
    			LL tmp2 = Qb.empty() ? INF : b[i] - Qb.top();
    			if (tmp1 <= tmp2 && tmp1 <= 0) {
    				tmp += tmp1; sz ++;
    				Qb.push(b[i]);
    				Qa.pop();
    			}
    			else if (tmp2 < tmp1 && tmp2 < 0){
    				tmp += tmp2;
    				Qb.pop();
    				Qb.push(b[i]);
    			}
    		}
    
    		if (sz >= m) {
    			ans = tmp + m * mid;
    			en = mid - 1;
    		}
    		else {
    			st = mid + 1;
    		}
    	}
    
    	cout << ans << endl;
    }
    

      

  • 相关阅读:
    【276】◀▶ Python 字符串函数说明
    Spring事务配置的五种方式 巨全!不看后悔,一看必懂!
    Android Developers:两个视图渐变
    《Linux命令行与shell脚本编程大全》 第二十七章 学习笔记
    Android的TextView与Html相结合的用法
    嵌入式C语言优化小技巧
    vxworks获取系统时间编程
    【算法与数据结构】在n个数中取第k大的数(基础篇)
    字符集转换 字符类型转换 utf-8 gb2312 url
    java 从零开始,学习笔记之基础入门<Oracle_基础>(三十三)
  • 原文地址:https://www.cnblogs.com/heisenberg-/p/6916520.html
Copyright © 2011-2022 走看看