大工之星编程挑战赛第一周题解
总结
去年出了一车BUG,今年希望举办的时候希望没有BUG。最后看来,榜看起来还是很好看的,没有人AK,前排过的很多,后排也有题过。榜单也没有出现断层,算是成功了。
其实出了一点小锅,幸亏问题不大(逃)
ileln眼中的题目难度:
A<B<C<H<I<F<D<J<G<E
题解
A.猛犸不上班
本场最签到的题。输出3即可。题面是整蛊的。
#include<iostream>
using namespace std;
int main() {
cout<<3<<endl;
return 0;
}
B.ileln的海鲜大餐
签到题,把所有数加起来就行了。
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N],n;
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=0;
for(int i=1;i<=n;i++) ans+=a[i];
cout<<ans;
return 0;
}
C.左特买镜子
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
double h1,h2,d;
int main(){
scanf("%lf%lf%lf",&h1,&h2,&d);
printf("%.4f %.4f %.4f",h1/2,(h1+h2)/2,h2/2);
return 0;
}
D.ileln想大吃一顿
基础背包问题,记三维i,j,k, d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示前i个食物,j长胖量,k含猪量的最大快乐值,食物含猪量范围是烟雾弹,大家不用关心。
更新即为:
d
p
[
i
]
[
j
]
[
k
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
[
k
]
,
d
p
[
i
−
1
]
[
j
−
w
[
i
]
]
[
k
−
p
[
i
]
]
)
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-w[i]][k-p[i]])
dp[i][j][k]=max(dp[i−1][j][k],dp[i−1][j−w[i]][k−p[i]])
最后统计第n位所有dp值的最大值即可。
注意更新时的背包预处理操作
对“动态规划背包问题”不了解的同学可以先上网学习下背包相关算法
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 2005
int n,K,P;
ll dp[N][N][11];
ll w[N],v[N],p[N];
int main(){
cin>>n>>K>>P;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&w[i],&v[i],&p[i]);
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=0;j<=K;j++){
for(int k=0;k<=P;k++){
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]);
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]);
}
}
for(int j=w[i];j<=K;j++){
for(int k=p[i];k<=P;k++){
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]);
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-w[i]][k-p[i]]+v[i]);
}
}
}
ll ans=0;
for(int i=0;i<=K;i++){
for(int j=0;j<=P;j++){
ans=max(ans,dp[n][i][j]);
}
}
cout<<ans;
return 0;
}
大家好,根据大工之星编程挑战赛规则,比赛中出现抄袭等作弊行为将被取消比赛成绩或禁赛,
严重者将通告学部按照大连理工大学违纪处理办法通知相关学部进行处分
请大家不要有侥幸心理,改个变量换个语言调整位置什么的,都会被查到!!!
E.左特照镜子
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
const double INF=100000.0;
const double eps=1e-6;
struct Ha{
double x,y;
Ha (double xx=0,double yy=0){
x=xx;y=yy;
}
Ha operator + (const Ha &a){
return Ha(x+a.x,y+a.y);
}
Ha operator - (const Ha &a){
return Ha(x-a.x,y-a.y);
}
}p,q,h,e,f,E;
double L=-1,R=-1;
void sym(){
if(p.x==q.x){
E.y=e.y;
E.x=2*p.x-e.x;
return;
}
double k=(p.y-q.y)/(p.x-q.x);
E.x=(e.x/k+p.x*k+e.y-p.y)/(k+1/k);
E.y=k*(E.x-p.x)+p.y;
E=E+E-e;
}
int judge(){
if(abs(p.x-q.x)<=eps)return -1;
double k=(p.y-q.y)/(p.x-q.x);
double Y=k*(E.x-p.x)+p.y;
if(Y>E.y)return -1;
else return 1;
}
double find(Ha a,Ha b){
double k=(a.y-b.y)/(a.x-b.x);
double Y=k*(f.x-a.x)+a.y;
return Y;
}
void H(int o){
if(abs(p.x-q.x)<=eps)
if(p.y*o<q.y*o)swap(p,q);
}
int main(){
scanf("%lf%lf%lf%lf",&p.x,&p.y,&q.x,&q.y);
scanf("%lf%lf%lf%lf%lf%lf",&h.x,&h.y,&f.x,&f.y,&e.x,&e.y);
if(p.x>q.x)swap(p,q);
if(p.y==q.y){
printf("0.0000");
return 0;
}
if(p.x==q.x&&p.x==f.x){
printf("0.0000");
return 0;
}
sym();
int pos=judge();
if(pos<0){
if(E.x<p.x){
if(f.x<=p.x){
L=INF;
R=INF;
}
else if(f.x>=q.x){
H(1);
L=find(E,q);
R=find(E,p);
}
}
else if(E.x>q.x){
if(f.x>=q.x){
L=INF;
R=INF;
}
else if(f.x<=p.x){
H(-1);
L=find(E,p);
R=find(E,q);
}
}
else{
if(f.x<=p.x){
L=find(E,p);
R=INF;
}
else if(f.x>=q.x){
L=find(E,q);
R=INF;
}
}
}
else{
if(E.x<p.x){
if(f.x<=p.x){
L=INF;
R=INF;
}
else if(f.x>=q.x){
L=find(E,p);
R=find(E,q);
}
else{
L=find(E,p);
R=find(p,q);
}
}
else if(E.x>q.x){
if(f.x<=p.x){
L=find(E,q);
R=find(E,p);
}
else if(f.x>=p.x){
L=INF;
R=INF;
}
else{
L=find(E,q);
R=find(p,q);
}
}
else{
if(f.x<=p.x){
L=-INF;
R=find(E,p);
}
else if(f.x>=p.x){
L=-INF;
R=find(E,q);
}
else{
L=-INF;
R=find(p,q);
}
}
}
L=max(L,0.0);
R=min(R,h.y);
if(L>=R)printf("0.0000");
else printf("%.4f",R-L);
return 0;
}
F.查字典-简单版
因为只是问是否出现,所以除了暴力匹配之外的算法都可以轻松通过,最简单的是利用自带find()函数,能找到就输出1,否则为0,很简单就不赘述了;
顺便给出了KMP和Hash和stl::map的解法
stl::find
#include <iostream>
#include <string>
using namespace std;
int main() {
int a, b;
string dict;
cin >> a >> b;
cin >> dict;
for (int i = 0; i < b; i++) {
string pattern;
cin >> pattern;
if (dict.find(pattern) != string::npos) {
cout << 1 << endl;
}
else {
cout << 0 << endl;
}
}
}
KMP
#include<bits/stdc++.h>
int n,q,len1,len2;
char a[100010],b[55];
int nxt[510];
using namespace std;
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",a+1);
len1=n;
while(q--) {
scanf("%s",b+1);
len2=strlen(b+1);
for(int i=1;i<=len2;i++) nxt[i]=0;
nxt[0]=0;nxt[1]=0;
int j=0;
for(int i=2;i<=len2;i++) {
while(j&&b[j+1]!=b[i]) j=nxt[j];
if(b[j+1]==b[i]) j++;
nxt[i]=j;
}
j=0;
int ans=0;
for(int i=1;i<=len1;i++) {
while(j&&b[j+1]!=a[i]) j=nxt[j];
if(b[j+1]==a[i]) j++;
if(j==len2) {
ans++;
break;
}
}
printf("%d\n",ans);
}
return 0;
}
HASH
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
const int Maxn=100005;
const int x=57;
unsigned long long ansB;
unsigned long long ansA[Maxn]={0};
unsigned long long X[Maxn];
int nxt[Maxn];
int sum=0,len1,len2;
char a[Maxn],b[55];
int n,q;
using namespace std;
void HashA()
{
len1=n;
ansA[len1+1]=0;X[0]=1;
for(int i=len1;i>=1;i--) {
ansA[i]=ansA[i+1]+(a[i]-'A'+1)*X[len1-i];
X[len1-i+1]=X[len1-i]*x;
}
}
void HashB()
{
len2=strlen(b+1);
ansB=0;
for(int i=1;i<len2;i++)
ansB=(ansB+b[i]-'A'+1)*x;
ansB+=b[len2]-'A'+1;
}
void find()
{
for(int i=1;i<=len1-len2+1;i++) {
unsigned long long temp1=ansA[i]-ansA[i+len2];
unsigned long long temp2=ansB*X[len1-len2-i+1];
if(temp1==temp2) {
printf("1\n");
return ;
}
}
printf("0\n");
return;
}
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",a+1);
HashA();
while(q--) {
scanf("%s",b+1);
HashB();
find();
}
return 0;
}
Map 复杂度较大,需开启 O 2 O_2 O2
#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define cinio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
int n,q;
unordered_map<string,bool>mp;
char s[N];
int main(){
cinio;
cin>>n>>q;
cin>>(s+1);
for(int i=1;i<=n;i++){
string now="";
for(int j=i;j<=min(n,i+50);j++){
now+=s[j];
mp[now]=1;
}
}
while(q--){
string u;
cin>>u;
if(mp[u]==1) cout<<1<<'\n';
else cout<<0<<'\n';
}
return 0;
}
感兴趣的同学可学习前置知识:
stl::find()
stl::map
KMP匹配算法
字符串hash算法
推荐阅读:
https://www.luogu.com.cn/blog/pks-LOVING/zi-fu-chuan-xue-xi-bi-ji-qian-xi-kmp-xuan-xue-di-dan-mu-shi-chuan-pi-post
https://blog.csdn.net/weixin_30510153/article/details/97461360
G.查字典-困难版
上面的KMP和Hash会超时,因此需要考虑换线性做法;
AC自动机和SAM自动机均可。注意本题中相同的模式串可能会重复多次,因此可以加一个link数组同步他们的答案(似乎被人用奇怪的哈希+map搞过去了(?)
出题人卡各种乱搞卡了一晚上,本来以为按照传统卡做法的点到为止我们已经防住了。
大意了没有闪,有的人不讲武德,来偷袭我19岁的老出题人。
我劝,这些年轻人,耗子尾汁,好好反思,今后多多去搞其他的出题人。
AC自动机版本
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<queue>
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"\n"
using namespace std;
int n,q;
int ans[4000005],cnt[4000005];
int link[4000005];
int sum,lst;
struct SAM{
int to[30],len,nxt;
SAM() {
this->nxt=0;
}
};
SAM tr[4000005];
void insert(int c)
{
int pos,cur=++sum;
ans[cur]=1;
tr[cur].len=tr[lst].len+1;
for(pos=lst;pos&&tr[pos].to[c]==0;pos=tr[pos].nxt) {
tr[pos].to[c]=cur;
}
if(!pos) tr[cur].nxt=1;
else{
int q=tr[pos].to[c];
if(tr[q].len==tr[pos].len+1) tr[cur].nxt=q;
else{
int cl=++sum;
tr[cl]=tr[q];
tr[cl].len=tr[pos].len+1;
tr[cur].nxt=tr[q].nxt=cl;
while(pos&&tr[pos].to[c]==q) {
tr[pos].to[c]=cl;
pos=tr[pos].nxt;
}
tr[cur].nxt=tr[q].nxt=cl;
}
}
lst=cur;
}
void pre()
{
for(int i=1;i<=sum;i++){
cnt[tr[i].len]++;
}
for(int i=1;i<=sum;i++) {
cnt[i]+=cnt[i-1];
}
for(int i=1;i<=sum;i++) {
link[cnt[tr[i].len]--]=i;
}
for(int i=sum;i;i--)
{
int p=link[i];
ans[tr[p].nxt]+=ans[p];
}
}
char tmp[105];
void work(){
while(q--) {
scanf("%s",tmp+1);
int pos=1,len=strlen(tmp+1);
bool vv=1;
for(int i=1;i<=len;i++) {
if(tr[pos].to[tmp[i]-'a']==0) {
printf("%d\n",0);
vv=0;
break;
}
else {
pos=tr[pos].to[tmp[i]-'a'];
}
}
if(vv) printf("%d\n",ans[pos]);
}
}
char a[1000005];
void input()
{
lst=1; sum=1;
scanf("%d%d",&n,&q);
scanf("%s",a+1);
for(int i=1;i<=n;i++) {
insert(a[i]-'a');
}
pre();
}
int main()
{
input();
work();
return 0;
}
大家好,根据大工之星编程挑战赛规则,比赛中出现抄袭等作弊行为将被取消比赛成绩或禁赛,
严重者将通告学部按照大连理工大学违纪处理办法通知相关学部进行处分
请大家不要有侥幸心理,改个变量换个语言调整位置什么的,都会被查到!!!
SAM版本
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<queue>
using namespace std;
int n,q,size=0;
char a[1000005],t[75];
int chr[1000005][30],nxt[1000005],fail[1000005],num[1000005];
int ans[1000005],link[1000005];
void insert(char ss[75],int k)
{
int len=strlen(ss+1),pos=0;
for(int i=1;i<=len;i++) {
int tmp=ss[i]-'a';
if(chr[pos][tmp]==0) {
size++;
chr[pos][tmp]=size;
}
pos=chr[pos][tmp];
}
link[k]=pos;
num[pos]=k;
}
void getfail()
{
memset(fail,0,sizeof(fail));
queue<int> q;
for(int i=0;i<26;i++) {
if(chr[0][i]) {
q.push(chr[0][i]);
fail[chr[0][i]]=0;
}
}
while(!q.empty()) {
int head=q.front();
q.pop();
for(int i=0;i<26;i++) {
if(chr[head][i]) {
fail[chr[head][i]]=chr[fail[head]][i];
q.push(chr[head][i]);
}
else {
chr[head][i]=chr[fail[head]][i];
}
}
}
}
void work()
{
int pos=0;
for(int i=1;i<=n;i++) {
pos=chr[pos][a[i]-'a'];
for(int j=pos;j;j=fail[j]) {
ans[num[j]]++;
}
}
}
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",a+1);
for(int i=1;i<=q;i++) {
scanf("%s",t+1);
insert(t,i);
}
getfail();
work();
for(int i=1;i<=q;i++) {
printf("%d\n",ans[num[link[i]]]);
}
return 0;
}
大家好,根据大工之星编程挑战赛规则,比赛中出现抄袭等作弊行为将被取消比赛成绩或禁赛,
严重者将通告学部按照大连理工大学违纪处理办法通知相关学部进行处分
请大家不要有侥幸心理,改个变量换个语言调整位置什么的,都会被查到!!!
没有学过ac自动机与后缀自动机的同学可先去学习下
推荐阅读:
https://63661.blog.luogu.org/solution-p3796
https://oi-wiki.org/string/sam/
https://www.luogu.com.cn/blog/xzyxzy/solution-p3804
H.ileln的大工食堂化计画
签到题,只需要看看相邻食堂之间的差值需要多少天来全部占领就行。
小心,你的学校可能没有食堂
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int n,m;
int cnt=0;
int p[N],a[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]==1){
p[++cnt]=i;
}
}
if(!cnt){
cout<<-1;return 0;
}
int ans=max(p[1]-1,n-p[cnt]);
for(int i=2;i<=n;i++){
int val=p[i]-p[i-1]-1;
if(val&1) val++;
val/=2;
ans=max(ans,val);
}cout<<ans;
return 0;
}
I.比赛前夜
题面描述了出题人在CCPC桂林前夜睡不着觉的痛苦遭遇
数据按时间先后给出,可以标记一个最近入睡的时间点,如果声音发生在时间点前,时间点顺延,如果在之后,说明睡了一会,答案增加,起床相当于一次声音,之后不再继续;需要注意,声音发生可能在起床后,这时需要忽略。
做法很多,合理即可
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<queue>
using namespace std;
int x,n,t[1005];
int main()
{
int ans=0,tmp=20;
scanf("%d%d",&x,&n);
for(int i=1;i<=n;i++) {
scanf("%d",&t[i]);
if(tmp<t[i]) ans+=min(60*x,t[i])-tmp;
tmp=max(tmp,t[i]+20);
if(tmp>60*x) break;
}
if(tmp<x*60) ans+=x*60-tmp;
printf("%d",ans);
if(ans==0) printf("\nStay Up Late.");
return 0;
}
大家好,根据大工之星编程挑战赛规则,比赛中出现抄袭等作弊行为将被取消比赛成绩或禁赛,
严重者将通告学部按照大连理工大学违纪处理办法通知相关学部进行处分
请大家不要有侥幸心理,改个变量换个语言调整位置什么的,都会被查到!!!
J.ileln的牛奶巧克力威化小饼干
线段树,对于一个区间有没有相同元素这样的问题,考虑预处理,to[i]表示下一个与i位置值相同的位置,对区间to数组求最小值,假如没有超过区间右端点,就是有相同元素,否则没有
修改的话其实考虑把所有相同的数维护成一个链表就好了,删掉一个数就相当于删掉链表的一个节点
to数组只需正着扫一遍即可
注意先区间离散化!当然stl::map也可以
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[500005];
struct ee{
int id,a,b;
};ee w[500005];
struct tree{
int to;
};tree t[2000005];
int head[1000005],from[500005],to[500005];
int cmt=0,cc=0;
int ans;
bool cmp1(ee a,ee b){
return a.a<b.a;
}
bool cmp2(ee a,ee b){
return a.id<b.id;
}
void build(int l,int r,int v){
if(l==r){
t[v].to=to[++cmt];
return;
}
int mid=(l+r)/2;
build(l,mid,2*v);
build(mid+1,r,2*v+1);
t[v].to=min(t[2*v].to,t[2*v+1].to);
}
void del1(int l,int r,int v,int ql,int qr){
if(r<ql||l>qr) return;
if(l==r){
if(l==ql){
to[ql]=to[to[ql]];
t[v].to=to[ql];
}
return;
}
int mid=(l+r)/2;
del1(l,mid,2*v,ql,qr);
del1(mid+1,r,2*v+1,ql,qr);
t[v].to=min(t[2*v].to,t[2*v+1].to);
}
void del2(int l,int r,int v,int ql,int qr){
if(r<ql||l>qr) return;
if(l==r){
if(l==ql) t[v].to=1000000;
return;
}
int mid=(l+r)/2;
del2(l,mid,2*v,ql,qr);
del2(mid+1,r,2*v+1,ql,qr);
t[v].to=min(t[2*v].to,t[2*v+1].to);
}
void query(int l,int r,int v,int ql,int qr){
if(r<ql||l>qr) return;
int mid=(l+r)/2;
if(ql<=l&&r<=qr){
ans=min(t[v].to,ans);
return;
}
query(l,mid,2*v,ql,qr);
query(mid+1,r,2*v+1,ql,qr);
}
int main(){
cin>>n>>q;
for(int i=1;i<=n;i++){
from[i]=0;
to[i]=1000000;
}
for(int i=1;i<=n;i++){
scanf("%d",&w[i].a);
w[i].id=i;
}
sort(w+1,w+n+1,cmp1);
for(int i=1;i<=n;i++){
if(w[i].a!=w[i-1].a) w[i].b=++cc;
else w[i].b=cc;
}
sort(w+1,w+n+1,cmp2);
for(int i=1;i<=n;i++) a[i]=w[i].b;
for(int i=1;i<=n;i++){
if(head[a[i]]){
to[head[a[i]]]=i;
from[i]=head[a[i]];
}
head[a[i]]=i;
}
build(1,n,1);
for(int i=1;i<=q;i++){
int opt;
scanf("%d",&opt);
if(opt==1){
int x;
scanf("%d",&x);
if(from[x]){
del1(1,n,1,from[x],from[x]);
}
if(to[x]!=1000000) from[to[x]]=from[x];
del2(1,n,1,x,x);
}
else{
int l,r;
scanf("%d%d",&l,&r);
ans=1000000;
query(1,n,1,l,r);
if(ans<=r) printf("NO\n");
else printf("YES\n");
}
}
}
K.左特想自习
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
char a[1000050];
int b[1000050],len=0;
int main(){
scanf("%s",a);
memset(b,0,sizeof(b));
while(a[len]>='0'&&a[len]<='9')len++;
for(int i=0;i<len;i++){
b[i]=a[i]-'0';
}
int no=0;
for(int i=0;i<len;i++){
no=no*10+b[i];
no=no%101;
}
if(no)printf("yes");
else printf("no");
return 0;
}
大家好,根据大工之星编程挑战赛规则,比赛中出现抄袭等作弊行为将被取消比赛成绩或禁赛,
严重者将通告学部按照大连理工大学违纪处理办法通知相关学部进行处分
请大家不要有侥幸心理,改个变量换个语言调整位置什么的,都会被查到!!!