武汉大学的校赛网络赛,之前WHUOJ上说网络赛晋级前20的外校队伍可以到武汉大学现场参加正式比赛,于是就参加了这场网络赛,结束的时候,由于罚时多了些,并没有进入前20,后来官网公布外校队伍名单,发现6题及以上的都晋级了,KILLBILL与Matrix在列,也是蛮surprise的~
水题还是蛮多的,因为剩下的难题都没过...
B题:
10^6长的数字字符串,最多只有一次操作:选取区间[l,r],区间中所有元素xi变为(10-xi)%10 ,问数字字符串的最大元素和。
类似最大子串和的求法
以Sample Input 1为例:
a : 3 7 7 5 0 5 3 8 0 8
b : 7 3 3 5 0 5 7 2 0 2
增量: 4 -4 -4 0 0 0 4 -6 0 -6
dp : 0 0 -4 0 0 0 4 -2 0 -6
Ans = ∑{a} + max{dp}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
const int M = 1000100;
char str[M];
int a[M];
int dp[M];
int main() {
int n;
while(~scanf("%d",&n)) {
scanf("%s",str);
int len = strlen(str);
int sum = 0;
for(int i=0; i<len; i++) {
a[i] = (10-(str[i]-'0'))%10 - (str[i]-'0');
sum += (str[i]-'0');
}
dp[0] = max(a[0],0);
for(int i=1; i<len; i++) {
dp[i] = max(dp[i-1]+a[i],a[i]);
}
int maxn = 0;
for(int i=0; i<len; i++) {
maxn = max(maxn,dp[i]);
}
printf("%d\n",sum+maxn);
}
return 0;
}
/**
10
3775053808
10
2580294019
10
4701956095
10
8888822222
*/
C题:
N点M边的图,Q次操作:
Ai和Bi加一条权值为Wi的边,求出当前图的最小生成树.(N是10^3,M是10^5,Q是10^3)
将原图求一棵MST,然后Q次操作都对这棵生成树操作。
如果Ai和Bi之间原来有边,且Wi比原边权大,那么不需要更新边
如果Ai和Bi之间原来有边,且Wi比原边权小,那么更新边,生成树权值=原来生成树权值-原AiBi边权+Wi
如果Ai和Bi之间原来无边,加上边之后,一定会出现一个环,找出这个环上的最大边权,然后把这个边权删掉就变成了新的MST
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
class Tarjan_U {
typedef int typec;
static const int ME=20048;
static const int MV=1024;
struct Q {
int u,v;
typec w;
} now;
vector<Q> qiao;
int Bcnt,Index,num[MV],dfn[MV],low[MV],belong[MV];
stack<int> s;
void addqiao(int u,int v,typec w) {
now.u=u;
now.v=v;
now.w=w;
qiao.push_back(now);
}
void tarjan(int u) {
s.push(u);
dfn[u]=low[u]=++Index;
int v;
for (int i=g.head[u]; ~i; i=g.e[i].next) {
if(g.e[i].vis)continue;
g.e[i].vis=g.e[i^1].vis=true;
v=g.e[i].v;
if (!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v])
addqiao(u,v,g.e[i].w);
} else low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]) {
Bcnt++;
do {
v=s.top();
s.pop();
belong[v]=Bcnt;
num[Bcnt]++;
} while(v!=u);
}
}
public:
struct G {
struct E {
int v,next;
bool vis;
typec w;
} e[ME];
int le,head[MV];
void init() {
le=0;
mt(head,-1);
}
void add(int u,int v,typec w) {
e[le].vis=false;
e[le].v=v;
e[le].w=w;
e[le].next=head[u];
head[u]=le++;
}
} g;
void init() {
g.init();
Index=Bcnt=0;
mt(num,0);
mt(dfn,0);
mt(low,0);
qiao.clear();
while(!s.empty()) s.pop();
}
void add(int u,int v,typec w) {
g.add(u,v,w);
g.add(v,u,w);
}
void solve(int n) {
for(int i=1; i<=n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
}
int getbcnt() {
return Bcnt;
}
int getbelong(int id) {
return belong[id];
}
int getnum(int id) {
return num[id];
}
} T;
struct Edge {
int u,v,w;
} edge[100010],now[2040];
int len;
class Kruskal {
typedef int typec;
static const int ME=100024;
static const int MV=1024;
class UnionFindSet {
int par[MV];
public:
void init() {
mt(par,-1);
}
int getroot(int x) {
int i=x,j=x,temp;
while(par[i]>=0) i=par[i];
while(j!=i) {
temp=par[j];
par[j]=i;
j=temp;
}
return i;
}
bool unite(int x,int y) {
int p=getroot(x);
int q=getroot(y);
if(p==q)return false;
if(par[p]>par[q]) {
par[q]+=par[p];
par[p]=q;
} else {
par[p]+=par[q];
par[q]=p;
}
return true;
}
} f;
struct E {
int u,v;
typec w;
friend bool operator < (E a,E b) {
return a.w<b.w;
}
} e[ME];
int le,num,n;
typec res;
public:
void init(int tn) {
n=tn;
le=res=0;
f.init();
num=1;
}
void add(int u,int v,int w) {
e[le].u=u;
e[le].v=v;
e[le].w=w;
le++;
}
typec solve() {
sort(e,e+le);
for(int i=0; i<le&&num<n; i++) {
if(f.unite(e[i].u,e[i].v)) {
num++;
res+=e[i].w;
now[len].u=e[i].u;
now[len].v=e[i].v;
now[len].w=e[i].w;
len++;
}
}
if(num<n) res=-1;
return res;
}
} gx;
const int M=1024;
bool vis[M];
int main() {
int n,m,q,u,v,w;
while(~scanf("%d%d%d",&n,&m,&q)) {
for(int i=0; i<m; i++) {
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
int ans=-1;
while(q--) {
scanf("%d%d%d",&u,&v,&w);
if(ans==-1) {
gx.init(n);
for(int i=0; i<m; i++) {
gx.add(edge[i].u,edge[i].v,edge[i].w);
}
gx.add(u,v,w);
len=0;
ans=gx.solve();
printf("%d\n",ans);
continue;
}
if(u==v) {
printf("%d\n",ans);
continue;
}
int sid=-1;
for(int i=0; i<n-1; i++) {
if((u==now[i].u&&v==now[i].v)||(v==now[i].u&&u==now[i].v)) {
sid=i;
break;
}
}
if(sid!=-1) {
if(w<now[sid].w) {
ans=ans-now[sid].w+w;
now[sid].w=w;
}
printf("%d\n",ans);
continue;
}
now[n-1].u=u;
now[n-1].v=v;
now[n-1].w=w;
T.init();
for(int i=0; i<n; i++) {
T.add(now[i].u,now[i].v,now[i].w);
}
T.solve(n);
int bcnt=T.getbcnt();
int bid=1;
for(int i=1; i<=bcnt; i++) {
if(T.getnum(i)>1) {
bid=i;
break;
}
}
for(int i=1; i<=n; i++) {
vis[i]=false;
}
for(int i=1; i<=n; i++) {
if(T.getbelong(i)==bid) {
vis[i]=true;
}
}
int big=0;
int tou,tov;
for(int i=0; i<n; i++) {
if(vis[now[i].u]&&vis[now[i].v]) {
if(big<now[i].w) {
big=now[i].w;
tou=now[i].u;
tov=now[i].v;
}
}
}
ans=0;
int id=0;
for(int i=0; i<n; i++) {
u=now[i].u;
v=now[i].v;
w=now[i].w;
if((u==tou&&v==tov)||(v==tou&&u==tov)) {
id=i;
continue;
}
ans+=w;
}
swap(now[id],now[n-1]);
printf("%d\n",ans);
}
}
return 0;
}
/**3 3 3
1 2 3
1 2 4
2 3 5
1 2 4
2 3 4
*/
D题:
用最少的矩阵把这玩意儿围起来
贪心方法,先扫一遍两个0中间的区间中的最大值,然后用矩阵来围起来,每一个元素减去这个最大值,然后重复上述步骤直到所有的元素都减成0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define mt(a,b) memset(a,b,sizeof(a))
#define LL long long
using namespace std;
const int inf = 0x3f3f3f3f;
int n;
int a[1234];
int main(){
while(~scanf("%d",&n)){
a[0] = 0;
a[n+1] = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
bool flag = true;
int ans = 0;
while(flag){
flag = false;
int s = -1;
int e = -1;
for(int i=0;i<=n;i++){
if(a[i]!=0 && a[i-1]==0 && s==-1){
s = i;
}
if(a[i]!=0 && a[i+1]==0 && s!=-1){
e = i;
// printf("--->%d %d\n",s,e);
int minn = inf;
for(int j=s;j<=e;j++){
minn = min(minn,a[j]);
}
ans ++;
flag = true;
for(int j=s;j<=e;j++){
a[j] -= minn;
}
i=e;
s = -1;
e = -1;
}
}
}
printf("%d\n",ans);
}
return 0;
}
F题:
长度为20的数字,选一个位置,截为前后两段,使得前后差值最小(绝对值)
如果单纯的认为从中间截断是错误的,因为对于140000,最好的方案是14-0000,而不是140-000
枚举截断的位置,然后高精度进行运算
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define LL long long
#define inf 0x3f3f3f3f
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
const int M = 50;
char str[M];
class Hp { //高精度类
int len,s[M];
public:
void init() {
len=1;
mt(s,0);
}
void init(char ch[]) {
len=1;
mt(s,0);
int i=0;
while(ch[i]=='0'&&ch[i]!=0) i++;
if(ch[i]!=0) {
len=strlen(ch)-i;
int pre = 0;
for(int j=strlen(ch)-1; j>=i; j--) {
s[pre]=ch[j]-48;
pre++;
}
}
}
void init(int x) {
len=1;
mt(s,0);
while(x) {
s[len-1]=x%10;
x/=10;
if(x) len++;
}
}
void init(const Hp& a) {
len=a.len;
for(int i=0; i<M; i++) {
s[i]=a.s[i];
}
}
void print() { //输出
int i=len-1;
while(s[i]==0&&i>0) i--;
for(; i>=0; i--) {
printf("%d",s[i]);
}
}
int cmp(Hp a) { //比较,类似于char数组比较
if(len<a.len) return -1;
else if(len>a.len) return 1;
else {
int temp=len-1;
while(temp>=0&&s[temp]==a.s[temp]) temp--;
if(temp==-1) return 0;
else if(s[temp]<a.s[temp]) return -1;
else return 1;
}
}
void subtract(Hp a) { //高精度减高精度
for(int i=0; i<len; i++) {
s[i]-=a.s[i];
if(s[i]<0) {
s[i]+=10;
s[i+1]--;
}
}
while(len>1&&s[len-1]==0) len--;
}
};
int main() {
Hp A,B,Cha;
char a[25],b[25];
int n;
while(~scanf("%s",str)) {
Hp Ans;
Ans.init(str);
int len = strlen(str);
for(int i=0; i<len-1; i++) {
mt(a,0);
mt(b,0);
for(int j=0; j<=i; j++) {
a[j] = str[j];
}
int pre = 0;
for(int j=i+1; j<len; j++) {
b[pre] = str[j];
pre++;
}
A.init(a);
B.init(b);
// puts("**************************");
puts(a);
puts(b);
// A.print();
// puts("");
// B.print();
// puts("");
// puts("**************************");
///A<B
if(A.cmp(B)==-1) {
B.subtract(A);
// B.print();
// puts("***");
if(B.cmp(Ans)==-1) {
Ans.init(B);
}
} else {
A.subtract(B);
// A.print();
// puts("****");
// A.print();
// puts("");
// B.print();
// puts("");
if(A.cmp(Ans)==-1) {
Ans.init(A);
}
}
}
Ans.print();
puts("");
}
return 0;
}
/**
457
75462
734794666
12345678900987654321
*/
///113580246
G题:
N个数选M个,是的方差最小。1 <= M <= N <= 30
要是方差最小,那么数越连续越好
将N个数排个序
O(N)遍历,每次拿连续的M个,更新方差最小值
PS:一开始考虑到精度问题,后来尝试交了一发,把数据水过去了
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef double lb;
int a[64];
lb b[64];
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
for(int i=0;i<n;i++){
b[i]=a[i];
}
lb ans=1e22;
for(int i=0;i<n;i++){
if(i+m>n) break;
lb sum=0;
for(int j=i;j<i+m;j++){
sum+=b[j];
}
lb avg=sum/m;
lb s=0;
for(int j=i;j<i+m;j++){
s+=(b[j]-avg)/m*(b[j]-avg);
}
ans=min(ans,s);
}
printf("%.3f\n",ans);
}
return 0;
}
H题:
又是我最最讨厌的小学追击类问题,类似于校赛上的“鹊桥相会”
上小学的Alice,在平时,她的拔拔Fzz会在正好在放学时开车到达学校,然后接上Alice就回家
有一天学校提前放学了,Alice发现比平时早了x分钟,然后Alice就开始步行回家,走了y分钟后,遇到了Fzz,然后上车回家,到家后发现比以往到家早了z分钟
现在给出x,y,z的任意两个值,求出剩下的一个值
设平时放学时间为t0,平时到家时间为t1,Alice速度为v,Fzz开车速度为V
(1)根据学校到家的距离,有vy+(t1-z-t0+x-y)V=(t1-t0)V
---> v/V = (z-x+y)/y
(2)考虑到节省的z时间,其实节省的是Fzz从Alice遇到Fzz位置到学校距离的开车来回一趟时间,有2vy=Vz
---> v/V = z/2y
∴(z-x+y)/y = z/2y ---> z/2 = x-y
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define mt(a,b) memset(a,b,sizeof(a))
#define LL long long
using namespace std;
const int inf = 0x3f3f3f3f;
int cal(char str[]){
int len = strlen(str);
int res = 0;
for(int i=0;i<len;i++){
res = res*10+(str[i]-'0');
}
return res;
}
int main(){
int _;
char str[20];
while(~scanf("%d",&_)){
while(_--){
int x = -1;
int y = -1;
int z = -1;
scanf("%s",str);
if(str[0] != '?'){
x = cal(str);
}
scanf("%s",str);
if(str[0] != '?'){
y = cal(str);
}
scanf("%s",str);
if(str[0] != '?'){
z = cal(str);
}
// printf("--->%d %d %d\n",x,y,z);
if(x==-1){
printf("%d\n",z/2+y);
continue;
}
if(y==-1){
printf("%d\n",x-z/2);
continue;
}
if(z==-1){
printf("%d\n",(x-y)*2);
}
}
}
return 0;
}
/**
3
? 30 20
40 ? 20
40 30 ?
*/