CF 254E(从当前向后递推)

E. 宿舍分享
time limit per test

1 second

memory limit per test

256 megabytes

input

input.txt

output

output.txt

一个学期有 n 天.第i天早上Vasya会带 ai kg食物来学校(食物最多放2天,今天和明天).

Vasya 每天吃 v kg 食物.他带来的食物原来够吃,但是Vasya 的 m 个室友会吃他的,编号
1 到 m. 第  j 个室友会在 lj到 rj
天和他一起住且每天吃 fj kg食物.
Vasya 每天可以给同住的人  j  fj kg食物(可以选一些人分).Vasya每一次分食物都要让人吃饱(要不然不厚道).

Vasya 怎样分出最多次食物?

Input

第一行2个整数 n 和 v (1 ≤ n, v ≤ 400).
第2行 n 个整数 a1, a2, ..., an (1 ≤ ai ≤ 400), 

第三行一个整数 m (1 ≤ m ≤ 400). 接下来 m 行每行3个整数.第
 j 行为 lj, rj, fj (1 ≤ lj ≤ rj ≤ n, 1 ≤ fj ≤ 400).

Output

第一行输出最多分出食物次数. 接下来 n 行第一行输出第 天分几次.接下来输出当天分的朋友的编号(任意顺序).若有多解任意输出一种.

Sample test(s)
input
4 1
3 2 5 4
3
1 3 2
1 4 1
3 4 2
output
7
1 2
1 2
3 2 1 3
2 2 3

Dp题,主要推方程。

显然每天从食量最小的贪起。

为了处理食物隔夜问题,多留一维表示昨天剩下的食物。

由于不好求出向前的方程,也不知道终态,所以要从当前向后递推(而非从前面向当前递推,类似矿工食堂的解决方案)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iostream>
#include<vector>
using namespace std;
#define MAXN (400+10)
#define MAXV (400+10)
#define MAXAi (400+10)
#define MAXM (400+10)
#define TomorrowFood (min(a[i],x))
#define _Next_ [i+1][TomorrowFood]
int n,m,v;
vector <pair< int,int> > man[MAXN]; // ith day jth man eat k food. //pair(j,k)
int a[MAXN]={0};
int f[MAXN][MAXV*2]={0}; // ith day havv j food left last night,->rat
int totlastf[MAXN][MAXV*2]={0}; //昨晚几个人吃
int last[MAXN][MAXV*2]={0};   //昨晚剩多少
int solve(int i,int j)
{
	if (i>2) solve(i-1,last[i][j]); //{ n+1->n n->n-1 ... 2->1 }   1->0
	cout<<totlastf[i][j];
	for (int k=1;k<=totlastf[i][j];k++)
		cout<<' '<<man[i-1][k].second;
	cout<<endl;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	cin>>n>>v;
	for (int i=1;i<=n;i++){cin>>a[i]; man[i].push_back(make_pair(0,0)); }
	cin>>m;
	for (int i=1;i<=m;i++)
	{
		int l,r,fi; cin>>l>>r>>fi; //fi=food in a man
		for (int j=l;j<=r;j++)
			man[j].push_back(make_pair(fi,i));
	}
	for (int i=1;i<=n;i++) sort(man[i].begin(),man[i].end());

	memset(f,128,sizeof(f));
	f[1][0]=0;

	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<=a[i-1];j++) //昨晚最多留下a[i] food(其它过期)
		{
			int x=j-v+a[i];  //表示昨天剩下的,吃了v,又多了今天的a[i]
			if (x<0) continue; //一定够吃
			if (f[i+1][TomorrowFood]<f[i][j])
			{
				f[i+1][TomorrowFood]=f[i][j];
				totlastf[i+1][TomorrowFood]=0;
				last _Next_ =j;
			}
			for (int k=1;k<man[i].size();k++)
			{
				x-=man[i][k].first;
				if (x<0) break;
				if (f[i+1][TomorrowFood]<f[i][j]+k)
				{
					f[i+1][TomorrowFood]=f[i][j]+k;
					totlastf[i+1][TomorrowFood]=k;
					last _Next_ =j;
				}
			}
		}
	}

	int ans=-1,now;
	for (int j=0;j<=MAXV;j++)
		if (ans<f[n+1][j]) {ans=f[n+1][j]; now=j; }
	cout<<ans<<endl;
	solve(n+1,now);
	return 0;
}

CF 253D(矩阵-4角相等且矩阵权值有上限的矩阵数)

D. 4a矩阵
time limit per test

2 seconds

memory limit per test

256 megabytes

input

input.txt

output

output.txt

有一个 n × m 的矩阵, 行 1 到 n,
列1 to 
m.

4a矩阵定义:

  • 矩阵内最多有 k 个 "a" ;
  • 4个角相等,长宽大于1.

请在给定矩阵中数出有几个子矩阵是4a矩阵.

Input

第一行3个整数n, m, k (2 ≤ n, m ≤ 400; 0 ≤ k ≤ n·m).

接下来为矩阵.

Output

一个整数,表示4a矩阵数.

Sample test(s)
input
3 4 4
aabb
baab
baab
output
2
input
4 5 1
ababa
ccaca
ccacb
cbabc
output
1
Note

第一个样例的解 (2, 2) (3, 3),

(2, 1) 
(3, 4).

这题是状态压缩问题。

用s[i][j]=∑a[1][j]+a[2][j]+..+a[i][j]

那么我们只需要枚举3个量

行的上下,还有列的

显然r列的关系可以由r-1的关系推出。



#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cstdlib>
using namespace std;
#define MAXN (400+10)
#define MAXM (400+10)
int n,m,k;
char a[MAXN][MAXM];
int s[MAXN][MAXM];  //表示s[i][j]=a[1][j]+a[2][j]+..a[i][j]
int c[1<<8];
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);


	memset(s,0,sizeof(s));
	scanf("%d%d%dn",&n,&m,&k);
	for (int i=1;i<=n;i++)
	{
		gets(a[i]+1);
		for (int j=1;j<=m;j++)
		{
			s[i][j]=s[i-1][j]+(a[i][j]=='a');
		}
	}
	long long ans=0;
	/*
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
			cout<<s[i][j]<<' ';
		cout<<endl;
	}
	*/
	;
	for (int i=1;i<n;i++)
	{
		for (int j=i+1;j<=n;j++)
		{
			int l=1,tot=0;
			memset(c,0,sizeof(c));
			for (int r=1;r<=m;r++)
			{
				tot+=s[j][r]-s[i-1][r];
				while (tot>k)
				{
					tot-=s[j][l]-s[i-1][l];
					c[a[i][l]]-=(a[i][l]==a[j][l]);
					l++;
				}
				if (l<r&&a[i][r]==a[j][r]) ans+=c[a[i][r]];
				if (a[i][r]==a[j][r]) c[a[i][r]]++;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

CF 254C(易位构字法)

C. 易位构字法
time limit per test

1 second

memory limit per test

256 megabytes

input

input.txt

output

output.txt

异位构字就是把一个字符串的顺序改变,可以得到另一个字符串.

对字符串 s 和 t ,至少修改 s 多少位,能使 s 和 t 易位构字?如果有很多方法,输出 s 易位后字典序最小的。

Input

两行 st. 长度不超过 105 ,只有大写字母。

Output

第一行输出最小修改次数,第二行输出 s 修改后字典序最小的字符串。

Sample test(s)
input
ABA
CBA
output
1
ABC
input
CDBABC
ADCABD
output
2
ADBADC
Note

 第二个数据,s 可改成: "ADBADC", "ADDABC",
"CDAABD", "CDBAAD", "CDBADA",
"CDDABA", "DDAABC", "DDBAAC".
字典序最小的是 "ADBADC".

直接处理字符串。

program word;
const
   maxn=100000;
var
   s,t:ansistring;//->ansistring
   n,i,j,ans,size,head:longint;
   pass,a1,a2,deln:array['A'..'Z'] of longint;
   add:array[1..26] of char;
   addn:array[1..26] of longint;
   ic:char;
begin
   assign(input,'input.txt');
   assign(output,'output.txt');
   reset(input);
   rewrite(output);
   fillchar(a1,sizeof(a1),0);
   fillchar(a2,sizeof(a2),0);
   fillchar(pass,sizeof(pass),0);
   fillchar(deln,sizeof(deln),0);
   readln(s);
   readln(t);
   n:=length(s);
   for i:=1 to n do inc(a1[s[i]]);
   for i:=1 to n do inc(a2[t[i]]);

   size:=0;
   ans:=0;
   for ic:='A' to 'Z' do
      if a2[ic]>a1[ic] then
      begin
         inc(size);
         add[size]:=ic;
         addn[size]:=a2[ic]-a1[ic];
         inc(ans,addn[size]);
      end
      else if a2[ic]<a1[ic] then
      begin
         deln[ic]:=a1[ic]-a2[ic];
      end;

   writeln(ans);
   head:=1;
   for i:=1 to n do
   begin
      inc(pass[s[i]]);
      if deln[s[i]]>0 then
      begin
         if (add[head]<s[i]) or (deln[s[i]]>a1[s[i]]-pass[s[i]]) then
         begin
            dec(deln[s[i]]);
            s[i]:=add[head];
            dec(addn[head]);
            if addn[head]=0 then inc(head);
         end;
      end;
   end;
   writeln(s);
   close(input);
   close(output);
end.

CF 254B(日期)

B. 评委会
time limit per test

1 second

memory limit per test

256 megabytes

input

input.txt

output

output.txt

2013年要举办 n 场比赛,编号1 到 n. 每场比赛需要人准备(在开幕的前 ti 天(不包括开幕当天))

如果准备时间重复,一个人一天只能为一场比赛准备,请问最少需要雇多少人准备?

Input

第一行一个整数 n  (1 ≤ n ≤ 100). 接下来 n 行每行为midipi 和 ti —
开幕的月份,日期,每天需要的人数,准备天数 (1 ≤ mi ≤ 12di ≥ 11 ≤ pi, ti ≤ 100),输入顺序任意,一天可能有同时多场比赛开幕。

非润年,二月28天. 可能需要在2012年某天开始准备.

Output

输出最小人数。

Sample test(s)
input
2
5 23 1 2
3 13 2 3
output
2
input
3
12 9 2 1
12 8 1 3
12 8 2 2
output
3
input
1
1 10 1 13
output
1

直接模拟,注意日期换算。


Program jury;
var
   n,i,j,m,d,p,t,ans:longint;
   month:array[1..12] of longint=(31,28,31,30,31,30,31,31,30,31,30,31);
   a:array[-1000..1000] of longint;
begin
   assign(input,'input.txt');
   assign(output,'output.txt');
   reset(input);
   rewrite(output);
   read(n);
   fillchar(a,sizeof(a),0);
   for i:=1 to n do
   begin
      read(m,d,p,t);
      for j:=1 to m-1 do inc(d,month[j]);
      for j:=d-1 downto d-t do
         inc(a[j],p);
   end;
   ans:=0;
   for i:=-1000 to 1000 do
      if ans<a[i] then ans:=a[i];
   writeln(ans);

   close(input);
   close(output);
end.

CF 254A(重复的数)

A. 数字卡片
time limit per test

1 second

memory limit per test

256 megabytes

input

input.txt

output

output.txt

2n张卡片编号为1 .. 2n.卡片数字相同可凑成一对,能否凑完?

Input

第一行1个整数n (1 ≤ n ≤ 3·105).
第二行有2n个整数a1, a2, ..., a2n (1 ≤ ai ≤ 5000)
表示第i张卡片的数字。

Output

如果凑不完,请输出 -1.否则输出 n 行,每行一对对整数,表示第i张卡片与第j张凑成一对。

输出任意方案。

Sample test(s)
input
3
20 30 10 30 20 10
output
4 2
1 5
6 3
input
1
1 2
output
-1

直接用数组记录出现次数,每遇到一对,就扔入解中。

Program Cards;
const
   maxn=300000;
var
   n,i,p,size:longint;
   a:array[1..5000] of longint;
   q1,q2:array[1..maxn] of longint;
begin
   assign(input,'input.txt');
   assign(output,'output.txt');
   reset(input);
   rewrite(output);

   read(n);
   fillchar(a,sizeof(a),0);
   size:=1;
   for i:=1 to 2*n do
   begin
      read(p);
      if a[p]>0 then
      begin
         q1[size]:=a[p];q2[size]:=i;
         inc(size);
         a[p]:=0;
      end
      else a[p]:=i;
   end;
   if size<>n+1 then writeln('-1')
   else for i:=1 to n do writeln(q1[i],' ',q2[i]);

   close(input);
   close(output);
end.

CF 253C(找中转行)

C. 光标移动
time limit per test

1 second

memory limit per test

256 megabytes

input

input.txt

output

output.txt

文本编辑器有n行,每行k个字符(显然光标有k+1个位置,标记为1..k+1.

有4个操作:

  • "Up": 光标将向上移1行,列不变。如果上一行字符太少,则光标跑到行末。

  • "Down":
    光标将向下移1行,列不变。如果上一行字符太少,则光标跑到行末。

  • "Right": 光标向右移1格,操作不能在行末进行。

  • "Left":
    光标向左移1格,操作不能在行首进行。

    求出把光标从 (r1, c1) 移向(r2, c2)最小步数.

  • Input

    第一行一个整数 n (1 ≤ n ≤ 100) . 第二行 n个整数a1, a2, ..., an (0 ≤ ai ≤ 105),
    表示每行字符数. 第三行4个整数 r1, c1, r2, c2 (1 ≤ r1, r2 ≤ n, 1 ≤ c1 ≤ ar1 + 1, 1 ≤ c2 ≤ ar2 + 1).

    Output

    输出最小步数

    Sample test(s)
    input
    4
    2 1 6 4
    3 4 4 2
    
    output
    3
    
    input
    4
    10 5 6 4
    1 11 4 2
    
    output
    6
    
    input
    3
    10 1 10
    1 10 1 1
    
    output
    3
    
    Note

    第一组数据若为

    123

    12

    123s567

    1t345

    则最小操作为 "Left", "Down", "Left".

    显然除了直接移(先行后列)外,

    唯一减少操作数的方法便是向中间以外的行数移,以求降低列的移动数。

    一定要注意不是挪到每行直接取abs(行尾-c2)的,因为可能会有往返行尾的路上有更小的,直接取行尾可能拉近它和c2的距离,但是这种操作是不可行的。

    #include<cstdio>
    #include<functional>
    #include<algorithm>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    #define MAXN (10000+100)
    int a[MAXN],n;
    int abs2(int a,int b)
    {
    	if (a<b) return b-a;
    	else return a-b;
    }
    int main()
    {
    	freopen("input.txt","r",stdin);
    	freopen("output.txt","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	int r1,c1,r2,c2;
    	scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
    	int mi=c1,ans=0,tans=1000000000;
    	if (r1<r2)
    	{
    		ans=r2-r1;
    		for (int i=r1+1;i<=r2;i++) mi=min(mi,a[i]+1);
    		tans=min(tans,ans+abs(mi-c2));
    		for (int i=r1-1,cost=2,mii=mi;i>=1;i--,cost+=2)
    		{
    			mii=min(mii,a[i]+1);
    			tans=min(tans,ans+cost+abs(mii-c2));
    		}
    		for (int i=r2+1,cost=2,mii=mi;i<=n;i++,cost+=2)
    		{
    			mii=min(mii,a[i]+1);
    			tans=min(tans,ans+cost+abs(mii-c2));
    		}
    	}
    	else
    	{
    		ans=r1-r2;
    		for (int i=r1-1;i>=r2;i--) mi=min(mi,a[i]+1);
    		tans=min(tans,ans+abs(mi-c2));
    		for (int i=r2-1,cost=2,mii=mi;i>=1;i--,cost+=2)
    		{
    			mii=min(mii,a[i]+1);
    			tans=min(tans,ans+cost+abs(mii-c2));
    		}
    		for (int i=r1+1,cost=2,mii=mi;i<=n;i++,cost+=2)
    		{
    			mii=min(mii,a[i]+1);
    			tans=min(tans,ans+cost+abs(mii-c2));
    		}
    	}
    	cout<<tans<<endl;
    	return 0;
    }

    CF 253B(队列上维护2个指针后移)

    B. 实验误差
    time limit per test

    1 second

    memory limit per test

    256 megabytes

    input

    input.txt

    output

    output.txt

    小明做实验,测了n次结果,因为误差,这些结果不一样。

    至少修改多少结果,才能使最大的结果不超过最小的结果的2倍?

    Input

    第一行一个整数 n (2 ≤ n ≤ 105),第二行有n个数c1, c2, ..., cn表示结果(1 ≤ ci ≤ 5000)

    Output

    输出一个整数,表示最小修改次数。

    Sample test(s)
    input
    6
    4 5 3 8 3 7
    
    output
    2
    
    input
    4
    4 3 2 4
    
    output
    0
    
    Note

    第1个数据删除8,7,第二个数据本身就满足条件。

    排序,从小到大枚举最小数,并把后指针挪向最大的不用修改的数进行统计即可。


    #include<cstdio>
    #include<functional>
    #include<algorithm>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    #define MAXN (100000+100)
    int a[MAXN];
    int n;
    int main()
    {
    	freopen("input.txt","r",stdin);
    	freopen("output.txt","w",stdout);
    
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	sort(a+1,a+n+1);
    	a[n+1]=1000000;
    //	for (int i=1;i<=n;i++) cout<<a[i]<<' ';cout<<endl;
    	int t=1,ans=0;
    	for (int h=1;h<=n;h++)
    	{
    		while (a[h]*2>=a[t+1]) t++;
    		ans=max(ans,t-h+1);
    	}
    	cout<<n-ans<<endl;
    
    }
    



    CF 253A(最大间隔相异队列)

    A. 男孩女孩
    time limit per test

    1 second

    memory limit per test

    256 megabytes

    input

    input.txt

    output

    output.txt

    有n个男孩和m个女孩排队,我们希望相邻的2个孩子性别尽可能不同。

    请帮助它们排队。

    Input

    第1行有2个整数 n,m (1 ≤ n, m ≤ 100).

    Output

    请输出一行它们排队的队列,B表示男孩,G表示女孩.

    输出任意一种方案即可。

    Sample test(s)
    input
    3 3
    
    output
    GBGBGB
    
    input
    4 2
    
    output
    BGBGBB
    
    Note

    In the first sample another possible answer is BGBGBG.

    In the second sample answer BBGBGB is also optimal.

    分成2种情况考虑-男多女少和女多男少

    考虑以下2种情况:

    BGBGBB

    BGBGGG-->GBGBGG 显然后面间隔较多


    Program Boys;
    var
       n,m,i:longint;
    begin
       assign(input,'input.txt');
       assign(output,'output.txt');
       reset(input);
       rewrite(output);
       read(n,m);
       if n<m then
       begin
          for i:=1 to n do write('GB');
          for i:=n+1 to m do write('G');
       end
       else
       begin
          for i:=1 to m do write('BG');
          for i:=m+1 to n do write('B');
       end;
       writeln;
    
       close(input);close(output);
    end.

    ZOJ 1576(稳定婚姻系统-重名)

    Marriage is Stable

    Time Limit: 5 Seconds
    Memory Limit:
    32768 KB Special Judge


    Albert, Brad, Chuck are happy bachelors who are in love with Laura, Marcy, Nancy. They all have three choices. But in fact, they do have some preference in mind. Say Albert, he likes Laura best, but that doesn't necesarily mean Laura likes him. Laura likes
    Chuck more than Albert. So if Albert can't marry Laura, he thinks Nancy a sensible choice. For Albert, he orders the girls Laura > Nancy > Marcy.

    For the boys:

    Albert: Laura > Nancy > Marcy
    Brad: Marcy > Nancy > Laura
    Chuck: Laura > Marcy > Nancy

    For the girls:

    Laura: Chuck > Albert > Brad
    Marcy: Albert > Chuck > Brad
    Nancy: Brad > Albert > Chuck

    But if they were matched randomly, such as

    Albert <-> Laura
    Brad <-> Marcy
    Chuck <-> Nancy

    they would soon discover it's not a nice solution. For Laura, she likes Chuck instead of Albert. And what's more, Chuck likes Laura better than Nancy. So Laura and Chuck are likely to come together, leaving poor Albert and Nancy.

    Now it's your turn to find a stable marriage. A stable marriage means for any boy G and girl M, with their choice m[G] and m[M], it will not happen that rank(G, M) < rank(G, m[G]��and rank(M, G) < rank(M, m[M]).

    Input

    Each case starts with an integer n (1 <= n <= 500), the number of matches to make.

    The following n lines contain n + 1 names each, the first being name of the boy, and rest being the rank of the girls.

    The following n lines are the same information for the girls.

    Process to the end of file.

    Output

    If there is a stable marriage, print n lines with two names on each line. You can choose any one if there are multiple solution. Print "Impossible" otherwise.

    Print a blank line after each test.

    Sample Input

    3
    Albert Laura Nancy Marcy
    Brad Marcy Nancy Laura
    Chuck Laura Marcy Nancy
    Laura Chuck Albert Brad
    Marcy Albert Chuck Brad
    Nancy Brad Albert Chuck

    Sample Output

    Albert Nancy
    Brad Marcy
    Chuck Laura

    这题就是稳定婚姻系统,

    注意有同名的男女。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<functional>
    #include<algorithm>
    #include<queue>
    #include<iostream>
    #include<map>
    #include<string>
    using namespace std;
    #define MAXN (2000+10)
    int n;
    queue<int> q;
    map<string,int> hpos_male,hpos_female;
    
    int female_like[MAXN][MAXN];  //SIS
    int male_like[MAXN][MAXN];    //SSI
    int match[MAXN];
    string a[MAXN];
    int main()
    {
    //	freopen("zoj1576.in","r",stdin);
    	while (scanf("%d",&n)!=EOF)
    	{
    		hpos_male.clear();
    		hpos_female.clear();
    		memset(match,0,sizeof(match));
    		int size=n+1;
    		for (int i=1;i<=n;i++)
    		{
    			cin>>a[i];hpos_male[a[i]]=i; q.push(i);
    			for (int j=1;j<=n;j++)
    			{
    				string s;
    				cin>>s;
    				if (i==1)
    				{
    					hpos_female[s]=size;
    					a[size]=s;
    					size++;
    				}
    				male_like[i][j]=hpos_female[s];
    			}
    		}
    		for (int i=n+1;i<=2*n;i++)
    		{
    			string sg;
    			cin>>sg;
    			for (int j=1;j<=n;j++)
    			{
    				string s;
    				cin>>s;
    				female_like[hpos_female[sg]][hpos_male[s]]=j;
    			}
    		}
    		/*
    		for (int i=n+1;i<=2*n;i++)
    		{
    			for (int j=1;j<=n;j++)
    				cout<<female_like[make_pair(a[i],a[j])]<<' ';
    			cout<<endl;
    		}
    		*/
    		while (!q.empty())
    		{
    			int now=q.front();
    			q.pop();
    			for (int j=1;j<=n;j++)
    			{
    				int v=male_like[now][j];
    				if (!match[v])
    				{
    					match[now]=v;match[v]=now;//cout<<now<<' '<<v<<endl;
    					break;
    				}
    				else if (female_like[v][match[v]]>female_like[v][now])
    				{
    					q.push(match[v]);
    					match[match[v]]=0;
    					match[now]=v;match[v]=now;//cout<<now<<' '<<v<<endl;
    					break;
    				}
    			}
    		}
    		for (int i=1;i<=n;i++)
    		{
    			cout<<a[i]<<' '<<a[match[i]]<<endl;
    		}
    		printf("n");
    	}
    	return 0;
    }


    Tyvj P2079(Spfa)

    P2079 - 防御机制

    From tangjz    Normal (OI)
    总时限:10s    内存限制:128MB    代码长度限制:64KB

    背景 Background

    NOIp2012考后欢乐赛第三题

    描述 Description

      服务器的新防御系统快要建好了,但是当正在数据库升级的时候,突然有黑客入侵机房网络,但是就在那时,插网线的房间钥匙丢了。所以Admin开始做防御机房的工作。
      不过,由于访问某台电脑可经过其他电脑加速,但是不能访问途经电脑,而且单位时间内一台电脑只能发出一条指令,指令不含嵌套指令,Admin只能一台一台的建立防御,幸好如此,黑客也是一台一台入侵,不过二人都会选择最优方案进行操作。所以Admin在主控端(教师端)不停的巡察各台电脑的同时,要求你在简短的时间内帮Admin计算一下哪些电脑将必然被入侵成功,以便于Admin及时做出额外的保护措施,如果所有可能被入侵的电脑都能被保护,那么请计算出将所有连接在主控端上的电脑做好防御所需的总时间。
      注意:只有黑客的入侵时间短于Admin的防御时间,黑客才能成功入侵。

    输入格式 InputFormat

    第一行为两个正整数N,M,表示有N台电脑,M条网线。(黑客近似地看为第N台电脑,在机房外)
    第二行为N-2个正整数,表示每台电脑的访问时间t。
    第三行到第 M+2 行,每行三个整数u,v,w,表示电脑u和v之间有一条网线,使用该网线互相到达对方电脑需要w个单位时间。

    输出格式 OutputFormat

    输出共两行。
    如果有可能会防御失败的电脑,第一行输出"No",第二行升序输出这些电脑的编号(空格隔开)。
    否则第一行输出"Yes",第二行输出将所有连接在主服务器上的电脑做好防御所需的总时间。

    样例输入 SampleInput [复制数据]

    [Sample 1]
    4 6
    1 2
    1 2 10
    1 3 5
    1 4 1
    2 3 4
    2 4 1
    3 4 10
    
    [Sample 2]
    5 7
    1 1 1
    1 2 70
    1 3 70
    1 4 70
    1 5 70
    2 5 70
    3 5 70
    4 5 70

    样例输出 SampleOutput [复制数据]

    [Sample 1]
    No
    2
    
    [Sample 2]
    Yes
    213

    数据范围和注释 Hint

    样例解释:
    第一组:由于1->2:2,4->2:1,所以2号电脑会被入侵成功
    第二组:1到2,3,4点的时间均为71,5到2,3,4点的时间均为71,防御成功

    对于20% 的数据,1 <=  N  <= 1000 ,1 <=  M  <= 2000
    对于60% 的数据,1 <=  N  <= 5000 ,1 <=  M  <= 100000
    对于100%的数据,1 <=  N  <= 10000,1 <=  M  <= 200000
    对于100%的数据,1 <= t,w <= 10000,1 <= u,v <= N
    提示:由于学生贪玩,有可能某个电脑没有连接主机,却接入网络;当然也有部分同学上课被禁网了。

    时间限制 TimeLimitation

    1s

    来源 Source

    tjz

    这题就是Spfa.


    Program defence;
    const
       maxn=90000;
       maxm=500000;
       inf=2139062143;
    type
       dis_arr=record
                  s:longint;
                  d:array[1..maxn] of longint;
               end;
    var
       n,m,i,j:longint;
       w:array[1..maxn] of longint;
       head,edge,next,weight:array[1..maxm] of longint;
       size:longint;
       d1,d2:dis_arr;
       queue:array[1..6000000] of longint;
    
    Procedure addedge(u,v,w:longint);
    begin
       inc(size);
       edge[size]:=v;
       weight[size]:=w;
       next[size]:=head[u];
       head[u]:=size;
    end;
    Procedure addedge_main;
    var
       u,v,w:longint;
    begin
       read(u,v,w);
       addedge(u,v,w);addedge(v,u,w);
    end;
    Procedure spfa(var d:dis_arr);
    var
       i,j,now,p:longint;
    begin
       i:=1;j:=1;queue[1]:=d.s;
       fillchar(d.d,sizeof(d.d),127);d.d[d.s]:=0;
       while (i<=j) do
       begin
          now:=queue[i];
          p:=head[now];
          while (p<>0) do
          begin
             if (d.d[now]+weight[p]<d.d[edge[p]]) then
             begin
                inc(j);
                queue[j]:=edge[p];
                d.d[edge[p]]:=d.d[now]+weight[p];
             end;
             p:=next[p];
          end;
          inc(i);
       end;
    end;
    Procedure print;
    var
       i,j:longint;
       ans:int64;
       flag:boolean;
    begin
       flag:=false;    ans:=0;
       for i:=2 to n-1 do
       begin
          if d1.d[i]<>inf then ans:=ans+int64(w[i]+d1.d[i]);
          if (d1.d[i]>d2.d[i]) then
          begin
             if not(flag) then begin flag:=true; writeln('No'); end
             else write(' ');
             write(i);
          end;
       end;
       if not(flag) then
       begin
          writeln('Yes');
          writeln(ans);
       end
       else writeln;
    end;
    begin
    //   assign(input,'defence.in');
    //   reset(input);
       fillchar(head,sizeof(head),0);
       fillchar(edge,sizeof(edge),0);
       fillchar(next,sizeof(next),0);
       size:=0;
       read(n,m); d1.s:=1;d2.s:=n;
       for i:=2 to n-1 do read(w[i]);
       for i:=1 to m do
       begin
          addedge_main;
       end;
       spfa(d1);spfa(d2);
       print;
    end.