2018西南交通大学ACM新秀杯决赛题解与总结

 总结:
  本次比赛是我第一次参加的现场赛,以前只参加过一些网络赛,取得的名次倒还也不算太差。但在此次现场赛中,我却只作出两道题。究其缘由:不知为什么,相较于网络赛,此次比赛我十分慌张,不能静下来思考,作题时不能专一、集中精神;其次,编译器的不顺手更让我紧张加重。但这些其实都不应是理由,最主要的理由应该仍是个人实力不够,还须要更多的积累、练习。
  下来以后从新作了比赛时的题目,发现其实赛题难度不大,几乎没有考察算法,若是能冷静下来认真作的话,至少有7道题是没问题的。
  接下来咱们一道一道解决。
  
Problems
A Key
B 吕元数猜测
C 人类的本质
D A%B problem
E 黑珍珠号的诅咒(The Curse of the Black Pearl)
F 聚魂棺(Dead man’s Chest)
G 喵星网络赛
H N桥问题
I Array
ios

                    Problem A. Key
Time limit: 1000 ms
Memory limit: 256 MB
Input file: standard input
Output file: standard output

庶神有 n 个不一样的锁和对应的钥匙。如今他把钥匙打乱了,庶神有一个怪癖,他会把钥匙一个一个插进锁里再拧开。求刚好打开一把锁的状况数。
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (0≤𝑇≤105) 表示有 T 组测试数据。
对于每组则测试数据,
仅有一行包含一个正整数 𝑛 (1≤𝑛≤107) ,表明有 n 对钥匙和锁。
Output
对于每组测试数据,
输出一行包含一个整数,表示刚好打开一把锁的状况数。答案可能会很大,请将结果对 107+7 取模web

 解题思路:
  此题是一道简单的错排问题。要求刚好打开一把锁的状况数,则先肯定打开的锁:总共有n种状况。而后对剩下的n-1把锁进行错排:用f[i]表示i把锁错排的状况数,由递归的思想很容易得f[i]=(i-1)(f[i-1]+f[i-2])。最后的答案就为nf[n-1]。
  处理细节上将错排数提早预处理便可。算法

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
const int BASE=1e7+7,MAXN=1e7;
long long dp[MAXN+5];					//dp[i]:i个数错排的状况数 
int main(void){
	int T;scanf("%d",&T);
	dp[0]=1,dp[1]=0;
	for(int i=2;i<=MAXN;++i)
		dp[i]=((i-1)*(dp[i-1]+dp[i-2]))%BASE;
	int n;
	while(T--){
		scanf("%d",&n);
		printf("%lld\n",(n*dp[n-1])%BASE);
	}
	return 0;
}

 本题小结:
 此题单纯考察了错排,几乎没有变形,以前练习时遇到过一样类型甚至包括更多变形的题目,可是此次现场赛时却没有作出来这道题???
 或许是第一次参加现场赛比较慌张的缘故,但也绝对不应丢掉此题。此题一样暴露出了我在 组合数学 方面存在严重问题,需重视。数组

 
 

                   Problem B. 吕元数猜测
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

给定两个正整数 A 和 B
要求找出全部的整数对 (C, D)
知足 𝐴≤𝐶≤𝐵, 𝐴≤𝐷≤𝐵 且 AB+BA≤CD+𝐷𝐶 (分数运算,并不是整除)
Input
输入包含多组测试数据,
第一行为一个正整数 𝑇 (0≤𝑇≤20) 表示有 T 组测试数据。
对于每组则测试数据,
仅有一行包含两个整数 𝐴 (0<𝐴≤109) 和 𝐵 (0<𝐵≤109) 含义如题目描述所示
Output
对于每组测试数据,
第一行包含一个整数 X 表示所求整数对的数量,
接下来 X 行,每行包含一个所求整数对 (C,D)
将答案按 C 的大小从小到大进行排序后输出,若 C 大小相等,则 C 相等的整数对按 C 的大小从小到大排序。如 (3,4)<(3,5) , (3,4)>(2,4)网络

 解题思路:
  由题设条件易得,C<=D和D<=C是一组对称的结果,因此咱们假设C<=D。
  仔细观察便能获得不等式两端有着相同的形式:f(x)=x+1/x。
  根据A/B<=C/D<=1<=D/C<=B/A,所以有f(D/C)=f(C/D)<=f(A/B)=f(B/A),当且仅当A/B=C/D,即A=C,B=D时等号成立。
  再由不等式得等号成立因此A=C,B=D。再加上D<=C的状况即为所有答案。svg

#include <stdio.h>
int main(void){
	int T,a,b;scanf("%d",&T);
	while(T--){
		scanf("%d%d",&a,&b);
		if(a==b) printf("1\n%d %d\n",a,a);
		else printf("2\n%d %d\n%d %d\n",a,b,b,a);
	}
	return 0;
}

 本题小结:
 本题是此次比赛惟二作出的题中的一道,而且实际作题时思路也没有上述那么严谨,更多的是偏向于猜想:一样假设C<=D,获得了A/B<=C/D<=1<=D/C<=B/A。
 再看给出的不等式,移项有:B/A-D/C<=C/D-A/B。能够看出右式中两个数都小于等于1,左式两个数都大于等于1,则要想使不等号成立,需两大于1的数的差值小于两小于1的数的差值,显然缩小左式的值较右式更为困难,所以猜想不等号最多只有等号成立。
 这道“可贵”作对的题仍是猜对的,让我在比赛时又受了次打击。测试

 
 

                      Problem C. 人类的本质
Time limit: 1000 ms
Memory limit: 256 MB
Input file: standard input
Output file: standard output

众所周知,在某些比赛交流群里,老是能完美地体现人类的本质,这是漫长的看算法作算法题生涯中为数很少的乐趣之一。这原本无可厚非,可是在 SWJTU 比赛交流群里,这种行为严重影响了群主的平常生活,他不得不停下他的家长会来答疑,当他发现群成员并无问题而只是一些复读消息时,他生气了。因而群里出现了复读禁言机器人。
复读禁言机器人老是禁言最后一条复读消息的发送者。有些成员发现了这个现象但仍是打算皮一下,因此他们在发出消息的一段时间后又撤回了他们的消息。复读禁言机器人并非时时刻刻在线的(由于机器人也是有妹子的),因此在机器人禁言的老是目前屏幕上剩下的消息中,最后一条复读消息的发送者。
已经被禁言的成员是不会再复读而且不能再被禁言的。数据保证每次操做的合理性。
Input
输入只包含一组测试数据,
第一行为一个正整数 n 和 q (1≤𝑛≤𝑞≤105) ,表示 n 我的参与复读,今天有 k 次操做(有些成员可能不遵循复读基本法,他们复读了不止一次)。
接下来有 q 行,每行包含一个整数 𝑑 (1≤𝑑≤𝑛) 和一个字符串 s ( s 仅包含小写字母且长度不超过 10 )表示操做种类和执行操做的群成员的名字。
𝑑=0 表示群成员 s 撤回了他最近的一条复读消息。
𝑑=1 表示群成员 s 复读了一条消息。
𝑑=2 表示禁言机器人 s 禁言了一个成员。
Output
输出包含若干行,
每行包含一个字符串,表示对于每次 𝑑=2 时,被禁言成员的名字。spa

 解题思路:
  很容易想到用栈记录复读序列,同时“撤回一次”表示抵消一次禁言,那么“禁言一次”就能够表示抵消无穷次禁言。注意将成员的名字用整数表示。设计

#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
using namespace std;
const int MAXN=100005;
map<string,int> ID;
int n,q;
int wd[MAXN];							// 撤回次数 
vector<string> name;
stack<int> s;
int main(void){
	cin>>n>>q;
	string str;
	for(int i=0,d;i<q;++i){
		cin>>d>>str;
		if(d==1){
			if(ID.find(str)==ID.end()){
				ID[str]=name.size();
				name.push_back(str);
			}
			s.push(ID[str]);
		}
		else if(d==0) ++wd[ID[str]];
		else if(d==2){
			int id;
			while(wd[(id=s.top())]){
				--wd[id];s.pop();
			}
			wd[id]=MAXN;s.pop();
			cout<<name[id]<<endl;
		}
	}
	return 0;
}

 本题小结:
 代码AC不了,但实在找不到问题。code

 
 

                     Problem D. A%B problem
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

这道题是众所周知的真·签到题 A+B Problem 的进阶版,只不过加变成了取模。听起来挺简单的,那来作作吧!
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤2333) 表示有 T 组测试数据。
对于每组则测试数据,
仅有一行包含两个整数 𝐴 (1≤𝐴≤10233) 和 𝐵 (1≤𝐵≤2×109)。
Output
对于每组测试数据,
输出一行包含一个整数,即 A 除以 B 的余数。

 解题思路:纯模板题。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int L=300;  
string s1,s2;
int sub(int *a,int *b,int La,int Lb)  
{  
    if(La<Lb) return -1;//若是a小于b,则返回-1  
    if(La==Lb)  
    {  
        for(int i=La-1;i>=0;i--)  
            if(a[i]>b[i]) break;  
            else if(a[i]<b[i]) return -1;//若是a小于b,则返回-1  
  
    }  
    for(int i=0;i<La;i++)//高精度减法  
    {  
        a[i]-=b[i];  
        if(a[i]<0) a[i]+=10,a[i+1]--;  
    }  
    for(int i=La-1;i>=0;i--)  
        if(a[i]) return i+1;//返回差的位数  
    return 0;//返回差的位数  
  
}  
string div(string n1,string n2,int nn)//n1,n2是字符串表示的被除数,除数,nn是选择返回商仍是余数  
{  
    string s,v;//s存商,v存余数  
     int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;//a,b是整形数组表示被除数,除数,tp保存被除数的长度  
     fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);//数组元素都置为0  
     for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';  
     for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';  
     if(La<Lb || (La==Lb && n1<n2)) {  
            //cout<<0<<endl;  
     return n1;}//若是a<b,则商为0,余数为被除数  
     int t=La-Lb;//除被数和除数的位数之差  
     for(int i=La-1;i>=0;i--)//将除数扩大10^t倍  
        if(i>=t) b[i]=b[i-t];  
        else b[i]=0;  
     Lb=La;  
     for(int j=0;j<=t;j++)  
     {  
         int temp;  
         while((temp=sub(a,b+j,La,Lb-j))>=0)//若是被除数比除数大继续减  
         {  
             La=temp;  
             r[t-j]++;  
         }  
     }  
     for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;//统一处理进位  
     while(!r[i]) i--;//将整形数组表示的商转化成字符串表示的  
     while(i>=0) s+=r[i--]+'0';  
     //cout<<s<<endl;  
     i=tp;  
     while(!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>  
     while(i>=0) v+=a[i--]+'0';  
     if(v.empty()) v="0";  
     //cout<<v<<endl;  
     if(nn==1) return s;  
     if(nn==2) return v;  
} 
int main(void){
	int T;cin>>T;
	while(T--){
		cin>>s1>>s2;
		cout<<div(s1,s2,2)<<endl;
	}
	return 0;
}

 本题小结:
 本题提醒我比赛必定要记得带模板。

 
 

                    Problem E. 黑珍珠号的诅咒(The Curse of the Black Pearl)
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

故事发生在传说中海盗最活跃的 加勒比海(Caribbean Sea)。这片神秘的海域位于北美洲东南部,那里碧海蓝天,阳光明媚,海面水晶般清澈透明。
玩世不恭的 Jack Sparrow (Oh, I mean, Captain Jack Sparrow …) 是活跃在加勒比海上的年轻海盗,拥有使人闻风丧胆的 “黑珍珠号”(Black Pearl) 海盗船。对他来讲,最惬意的生活莫过于驾驶着 “黑珍珠号” 在加勒比海上游荡,自由自在地打劫过往船只。但不幸的是,他遭到了本身的大副 Barbossa 的背叛,不只本身珍爱的 “黑珍珠号” 被 Barbossa 偷走,本身也被遗弃在荒岛独自等死。
与此同时,抢劫了 “黑珍珠号” 的 Barbossa 变得更加猖狂,在加勒比海上横行霸道,一时成为整个加勒比海的霸主。但 Barbossa 和他的船员们却因掠取了 “阿兹特克金币”(Aztec Gold) 而遭到了邪恶的诅咒:成为没法知足一切欲望的骷髅,他们渴得要死却渴不死,他们饿得要死却饿不死,他们感不到海风拂面、他们触不到浪花拍打……
终于, Barbossa 得知,只有将 882 枚散落在世界各地的 “阿兹特克金币” 集齐,才可以消除诅咒,因而他派遣 Marty 去四处搜集散落的金币。
一天, Marty 在集市上看到了 n 枚闪闪发光的金币, Marty 并不知道这是否是能够解除他们诅咒的金币,因此 Marty 打算买一些回去碰碰运气。
这些金币被串成了一串,每枚金币都有各自的价格。众所周知, Marty 是一个丢三落四的家伙,他并不能带着散装的金币回去。也就是说,若是他打算购买金币的话,就只能买连续的、连成一串的若干枚。而在那个时候,扫码转帐等移动支付还并无普及开来,因此 Marty 只能用口袋里无数个价值为 a 的钱币,去换取疑似的 “阿兹特克金币” 。
在 Marty 打算交钱的时候,得知店主并无零钱找给他。因为 Marty 仍是一个极其小气的人,若是没有找零, Marty 是不可能用高价值的钱币去换取小价格金币的,即便那些金币有多是去除诅咒的关键。这就表示, Marty 买的某串金币价格的总和一定是 k 的倍数。
The 14th ACM-ICPC Southwest Jiaotong University Programming Contest Freshman Cup
Southwest Jiaotong University, Sunday December 9, 2018
Page 10 of 18
但 Marty 的数学能力也不好……他并不知道他可否找到某串合适的金币,因此他把这个问题交给了你……
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤10) 表示有 T 组测试数据。
对于每组测试数据,
第一行包含两个正整数 𝑛 (1≤𝑛≤105) 和 𝑘 (1≤𝑘≤5×104) ,表示集市有 n 枚金币, Marty 有无数个价值为 k 的钱币。
第二行包含 n 个正整数 𝑎𝑖 (0≤𝑎𝑖≤109) ,表示那 n 个连在一块儿的金币各自的价格。
Output
对于每组测试数据,
输出一行包含一个字符串表示结果,若是 Marty 能够买到某串金币,则输出 “YES” (不包含引号);不然输出 “NO” (不包含引号)。

 解题思路:
  本题能够看作判断序列中是否存在和为k的倍数的子串,直接暴力求解的话O(n^2)可能会超时,所以可以使用分治的策略。
  将一个序列分为左右两部分,一个序列符合要求表明左串(1)、右串(2)或者从序列中间跨过左右两边的子串(3)符合要求。下降复杂度主要考虑对(3)的处理,因为只须要判断子串的和是否为k的倍数,即为(3)中左半边的和加上右半边的和为k的倍数,即对左半边每一个和值查找在右半边中是否存在对应的值知足条件,所以咱们能够考虑用STL中的红黑树来实现,复杂度f(n)=O(nlogn)。
  写出时间复杂度的递推公式:
   T(n)=2T(n/2)+O(nlogn),由主定理得:T(n)=O(nlogn)。
   (须要注意的是,虽然理论值如此,可是实际中STL的运行速度很慢)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#include <set>
using namespace std;
int n,k,a[100005];				//a[i]从1到n储存	
int sum[100005];				//从1到i的子串和(mod k)
set<int> s;
bool Can(int i,int j){
	if(i==j) return a[i]%k==0;
	else if(j-i==1) return (a[i]+a[j])%k==0;
	int mid=(i+j)/2;s.clear();
	for(int p=mid+1;p<=j;++p)
		s.insert((sum[p]-sum[mid]+k)%k);
	for(int p=mid,x;p>=i;--p){
		x=(sum[mid]-sum[p-1]+k)%k;
		if(s.find(k-x)!=s.end())
			return true;
	}
	return Can(i,mid)||Can(mid+1,j);
}
int main(void){
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		bool flag=false;
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			if(a[i]==0) --i,--n;
			else if(a[i]%k==0) flag=true;
			else a[i]%=k;
		}
		if(n==0){
			puts("NO");continue;
		}
		for(int i=1;i<=n;++i){
			sum[i]=(sum[i-1]+a[i])%k;
			if(sum[i]==0){
				flag=true;
				break;
			}
		}
		if(flag==true||Can(1,n))
			puts("YES");
		else puts("NO");
	}
	return 0;
}

 本题小结:
 本题稍微考察了一点分治的算法,难度不大。实际上本题数据量不算大,比赛时用了O(n^2)的暴力方法加上必定的剪枝也过了。

 
 


                     Problem F. 聚魂棺(Dead man’s Chest)
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

Davy Jones 曾是英俊的北海海盗王,驾驶着 “飞翔的荷兰人号” (the Flying Dutchman) 在海上驰骋。和其余海盗同样, Davy Jones 也有着对冒险的热衷,有着对大海的痴狂,若是没有遇到那个她,他会和 Jack , Barbossa 同样,杀人,防火,抢劫,在 Tortugar 一手拿着朗姆酒瓶,一手拿着枪开怀地大笑……
但他恰恰遇到了,他遇到了那个她,海神 Calypso 。今后 Jones 像着了魔同样深深爱上了 Calypso 。也许 Calypso 只是玩弄,也许是真的也喜欢上了 Jones , Calypso 提出让 Jones 帮忙,替她照顾那些在海上死去的亡灵,将他们引渡到阴间。这个任务要在海上航行十年,才能有上岸的一天,与爱人 Calypso 相见。
为了深爱的人, Jones 答应了:他再也不逍遥四海,再也不烧杀掳掠,他放下杀戮、放弃自由,为了爱人他放弃了海盗的尊严。今后,纵情高歌他不参与,纷繁世俗与他无关,有的只是一我的,一条船,作一个引渡者,带着亡魂来往于阴间与人世。为了爱情,海盗也变得靠谱了起来。就这样,一天,两天、一年,又一年……十年虽长,但 Jones 不急,为了本身的深爱的人,他能够等,他认为这全部的一切是值得的……
然而十年终了,当 Jones 上岸来到他与 Calypso 相约的地方时,等待他的,却只有一席空地…… Jones 委屈,愤怒,不解,失望……黄昏将至,即将到来的又是一个漫长的十年。 Jones 的心里太过痛苦,他想结束这一切,他将本身的心脏挖出,连同情书一块儿锁进箱子里,藏在遥远的地方,他想发誓忘记 Calypso ,但他作不到…… 他放弃了做为引渡者的工做,并在大海上肆意杀戮,以掩盖本身内心的痛苦……
被他抓到的水手,只有两条路: 要么丧身大海,没有了引渡人,那就只能永远的作海上亡魂;要么成为 “飞翔的荷兰人号” 船员,在船上服役 100 年,届时,一定思想麻木、肉体腐败,船员的一部分也将成为船体的一部分。
一天,船员 系带王 Bill 因一个新来的家伙而和船长 Jones 赌博起来。赌的方法是扑克。不一样于咱们日常 13 种数字牌的扑克 {A,2,3,…,10,J,Q,K} ,这种扑克一共有 n 种数字 {1,2,3,…,n} 。
为了简化问题,咱们约定每一个人只能出 “对子” (即两张数字同样的牌) 和 “顺子” (即三张数字相连的牌) 。
现已知 Bill 的全部牌,请问他最多能够出牌几回。
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤10) 表示有 T 组测试数据。
对于每组测试数据,
第一行包含一个正整数 𝑛 (1≤𝑛≤105) 表示扑克有几种数字。
接下来一行包含 n 个正整数 𝑎𝑖 (0≤𝑎𝑖≤109),表示 Bill 有 𝑎𝑖 张数字为的 i 的扑克。
Output
对于每组测试数据,
输出一行包含一个字符串表示结果,若是 Marty 能够买到某串金币,则输出 “YES” (不包含引号);不然输出 “NO” (不包含引号)。

 解题思路:
  本题可运用贪心的思想。将出“对子”称为操做1,“顺子”为操做2。不难发现,除了11211这种状况外,优先操做1一定不会有亏损。所以当某种牌的数量大于2时,优先进行操做2,这样最后咱们能获得一个最大值为2的序列。
  而后咱们便要排除11211此种状况。咱们又能想到当出现“11*”此种状况时,优先操做2也一定不会有亏损,且对后续操做无后效性。这样咱们便获得了贪心的策略:先对全部数量大于2的牌进行操做1,获得一个数量最大为2的序列,而后对序列顺序处理,出现“11*”执行操做2,其他执行操做1。

#include <stdio.h>
#include <stdlib.h>
long long ans;
int a[100005],N;
int main(void){
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d",&N);
		ans=0;
		for(int i=0;i<N;++i){
			scanf("%d",&a[i]);
			if(a[i]){
				if(a[i]%2==0) ans+=(a[i]-2)/2,a[i]=2;
				else ans+=(a[i]-1)/2,a[i]=1;
			}
		}
		a[N]=a[N+1]=a[N+2]=0;
		for(int i=0,x=a[0],y=a[1],z=a[2];
			i<N;++i)
				if(x==1&&y==1&&z)
					++ans,x=y-1,y=z-1,z=a[i+3];
				else{
					if(x==2) ++ans;
					x=y,y=z,z=a[i+3];
				}
		printf("%lld\n",ans);
	}
	return 0;
}

 本题小结:
 比赛中看到本题我首先想到的是用dp,可是却找不到合适的状态,所以放弃了。比赛结束后忽然想到本题好像能够贪心,因而尝试了一下,可是我才疏学浅,没有想到如何用数学方法严格地证实贪心的结论,之后有空会再尽力尝试一下。

 
 

                   Problem G. 喵星网络赛
Time limit: 1000 ms
Memory limit: 256 MB
Input file: standard input
Output file: standard output

在遥远的喵星文明中,一天被划分为 n 小时。
所以,喵星被划分为 n 个时区, 相邻时区的时差为 1 小时。也就是说,当第 1 个时区的时间为 1:00 时,第 2 个时区的时间就为 2:00 , …… , 第 n 个时区的时间为 n:00 。
对于每一个时区的喵星人来讲,他们只会在当地的 2 点到 F 点之间活动(即从 S:00 开始, F:00 结束)。
喵星打算举办一场时长 1 小时的程序设计网络赛,从整点开始,已知第 i 个时区有 𝑎𝑖 只喵星人,请问何时开始,可以参加比赛的喵星人的数量最多? 输出答案为第 1 个时区的当地时间。
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤30) 表示有 T 组测试数据。
对于每组则测试数据,
第一行为一个正整数 𝑛 (2≤𝑛≤105) ,表明喵星一天有 n 个小时,被划分为 n 个时区;
第二行有 n 个数, 第 i 个数为 𝑎𝑖 (1≤𝑎𝑖≤104) ,表明第 i 个时区有 𝑎𝑖 只喵星人。
第三行输出两个整数 S 和 F (1≤𝑆≤𝐹≤𝑛) , 表明每一个时区喵星人的活动时间。
Output
对于每一个测试数据,
输出一行包含一个整数,表示在第 1 时区下,使可以参加比赛的喵星人数最多的比赛开始的整点时间。
0:00 用 n:00 表示。

 解题思路:
  本题可先将每一个时区的时间都转换为第1个时区的时间,转换公式为:第i个时区的t点等于第一个时区的(t-i+n)%n+1点。而后将每一个时区的活动时间转换为第一个时区的时间,获得S[i]和F[i],便容易看出此题是一个区间叠加类的问题。
  由于活动时间的区间都是连续的,因此可用f[i]表示从S[i]开始活动的总人数。令φ=S-F-1,则f[i]=sum(a[i],a[i+φ]),S>F。当f[i]最大时对应的S[i]即为答案。
  当S=F时,去答案为1。
  处理sum时,可用一维数组sum[i]表示从第1个时区到第i个时区的人数和,节省时间空间。
  复杂度O(N)。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int a[100005],f[100005];
int	S,F,n,sum[100005];
int main(void){
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;++i) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
		scanf("%d%d",&S,&F);
		if(S==F) puts("1");
		else{
			int t=F-S-1;
			for(int i=1;i<=n;++i)
				if(i+t<=n) f[i]=sum[i+t]-sum[i-1];
				else
					f[i]=sum[(i+t+n-1)%n+1]+sum[n]-sum[i-1];
			int p,m=0;
			for(int i=1;i<=n;++i)
				if(f[i]>m) p=i,m=f[i];				
			printf("%d\n",(S-p+n)%n+1);			
		}
	}
	return 0;
}

 本题小结:
 本题若说难度,主要就是题目读起来比较绕,比赛时读着读着就晕了,根本没有好好思考过。不过本题理解了以后作并不复杂。

 
 

                    Problem H. N桥问题
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

X 开车来到了一条河边,河上有 n 座互相平行的桥。 X 想按照指定的顺序经过全部的桥仅一次,可是又不想让陆上的车辙印交叉(过第一个桥前的车辙不算)。假设陆地平坦,面积无限大,车的大小和形状不计。 X 想知道对于给定的过桥顺序,他可否达成这样的目标。
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤10) 表示有 T 组测试数据。
对于每组则测试数据,
第一行包含一个正整数 𝑛 (1≤𝑛≤106) 表示桥的数量,
第二行包含 n 个互不相同的整数 𝑎𝑖 (1≤𝑎𝑖≤𝑛), 表示第 i 个桥须要第 𝑎𝑖 个经过。
数据保证全部 n 的和不超过 106。
Output
对于每一个测试数据,
输出一行表示答案,若是 X 能按照这样的次序经过,输出 “Yes” (不包含引号),不然输出 “No” (不包含引号)。
 解题思路:
  咳咳,,,,,还没作出来。

 
 

                    Problem I. Array
Time limit: 1000 ms
Memory limit: 128 MB
Input file: standard input
Output file: standard output

一个数列 A ,长度为 n ,编号从左至右为 1…n ,为了知足某种特定的规律, 𝑎𝑖 不超过 k ,且大于 1 。
如今有两种操做。
一、表明将整个数列的值加 1 ,即 𝑎𝑖 = 𝑎𝑖%𝑘+1 。
二、查询区间 [𝑙,𝑟] 中值为 X 的个数。
Input
输入包含多组测试数据,第一行为一个正整数 𝑇 (1≤𝑇≤5) 表示有 T 组测试数据。
对于每组则测试数据,
第一行三个数 𝑛 (1≤𝑛≤105), 𝑘 (1≤𝑘≤20) 和 𝑞 (1≤𝑛≤105) ,表明数组长度,值的上限和询问的次数。
第二行包含 n 个数表明 𝑎𝑖 的值。
接下来有 q 行有 q 次操做。
1 表明 操做1 。
2 l r x 表明 操做2 。
Output
对于每次询问输出所求值。

 解题思路:
  本题考察内容也较为简单,主要考察计算数组中某一区间某数的个数。为了节省时间和空间可用一个二维数组sum[i][k]表示从i到N区间中k的个数。对任一区间[i,j],num(k)=sum[i][k]-sum[j+1][k]。k最大为20,所以复杂度O(N)。
  须要注意的地方是对操做1的记录:只须要记录操做1的次数t,最后查询时的原始值为(x-t+k-1)%k+1。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
int k,q,N,a[100005];
int sum[100005][21];		//sum[i][j]= <从0到i数组中j的个数> 
int main(void){
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&N,&k,&q);
		for(int i=0;i<=k;++i) sum[N][i]=0;
		for(int i=0;i<N;++i) scanf("%d",&a[i]);
		for(int i=N-1;i>=0;--i)
			for(int j=1;j<=k;++j)
				if(a[i]!=j)	sum[i][j]=sum[i+1][j];
				else sum[i][j]=sum[i+1][j]+1;
		
		int num=0;			//操做1的次数
		for(int i=0,op;i<q;++i){
			scanf("%d",&op);
			if(op==1)	++num;
			else{
				int l,r,x;
				scanf("%d%d%d",&l,&r,&x);
				x=((x-(num%k)+k-1)%k)+1;
				printf("%d\n",sum[l-1][x]-sum[r][x]);
			}
		}
	}
	return 0;
}

 本题小结:  本题没有什么难点,思路也很天然。可是在比赛时不知道为何,处理sum数组时用了顺序处理:复杂度O(N^2)???因此妥妥的TLE。可能果然是太紧张了,作题时彻底没有进行思考。须要好好反思。