牛客網暑期ACM多校訓練營(第三場)
牛客網暑期ACM多校訓練營(第三場)
A. PACM Team
01背包,輸出方案,用bool存每種狀態下用的哪一個物品,卡內存。官方題解上,說用char或者short就行了。還有一種做法是把用的物品壓成一個int。
#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) #define pb push_back typedef long long ll; typedef long double LD; using namespace std; int n,p[37],a[37],c[37],m[37],g[37],A,C,M,P,ans = 0; int dp[37][37][37][37]; bool usd[37][37][37][37][37]; int v[40],cnt=0,vis[40]; int main() { int tp=0,ta=0,tc=0,tm=0,tmp=0; scanf("%d",&n); rep(i,0,n-1) { scanf("%d%d%d%d%d",&p[i],&a[i],&c[i],&m[i],&g[i]); } scanf("%d%d%d%d",&P,&A,&C,&M); dp[0][0][0][0] = tmp; rep(i,0,n-1) { per(ip,P,p[i])per(ia,A,a[i])per(ic,C,c[i])per(im,M,m[i]) { int t = 0,t2 = dp[ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]] + g[i]; t = dp[ip][ia][ic][im]; if(t <= t2) { dp[ip][ia][ic][im] = t2; usd[ip][ia][ic][im][i] = 1; } } } tp = P; ta = A; tc = C; tm = M; per(i,n-1,0) if(tp>=0&&ta>=0&&tc>=0&&tm>=0) { if(usd[tp][ta][tc][tm][i]) { v[++cnt]=i; tp-=p[i]; ta-=a[i]; tc-=c[i]; tm-=m[i]; } } sort(v+1,v+1+cnt); printf("%d\n",cnt);int f=0; if(cnt) { for(int i=1;i<=cnt;++i) { if(f)printf(" ");f=1; printf("%d",v[i]); }puts(""); } return 0; }
C. Shuffle Cards
每次把中間一段數字移到開頭。學習了rope的用法。然後寫了個塊鏈。。。t到爆炸。
rope:
push_back(x);//在末尾添加x
insert(pos,x);//在pos插入x,自然支持整個char數組的一次插入
erase(pos,x);//從pos開始刪除x個
copy(pos,len,x);//從pos開始長度為len用x代替
replace(pos,x);//從pos開始換成x
substr(pos,x);//提取pos開始x個
at(x)/[x];//訪問第x個元素
rope做法:
#include <bits/stdc++.h> #include <ext/rope> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define pb push_back typedef long long ll; const int N = 1e6 + 7; using namespace std; using namespace __gnu_cxx; int n,m; rope<int> s; int main() { scanf("%d%d",&n,&m); rep(i,1,n) s.pb(i); rep(i,1,m) {int p,x; scanf("%d%d",&p,&x); s = s.substr(p-1,x) + s.substr(0,p-1) + s.substr(p+x-1,n-(p+x-1)); } int f=0; rep(i,0,n-1) { if(f)printf(" ");f=1; printf("%d",s[i]); }puts(""); return 0; }
塊鏈做法:
//listblock TLE #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) #define pb push_back typedef long long ll; const int N = 300010; const int M = 1000 + 7; using namespace std; int n,m,B,num,hd; struct listBlock{ vector<int> s; int nxt,pre; void init(){s.clear();nxt=pre=0;} void ins(int x) {s.pb(x);} }block[N]; void init(int s[]) { B = sqrt(n); num = n/B; if(n%B)++num; hd=0; block[hd].nxt = 1; rep(i,1,num) block[i].init(),block[i].nxt=i+1,block[i].pre=i-1; block[num].nxt = -1; rep(i,0,n-1) block[i/B+1].ins(s[i]); } void split(int x,int p) {//(l[x],p)(p+1,r[x]) if(p==(int)block[x].s.size()-1)return; int n = (int)block[x].s.size(); listBlock tmp = block[x]; block[x].init(); rep(i,0,p)block[x].ins(tmp.s[i]); block[x].pre = tmp.pre; block[x].nxt = num+1; ++num; block[num].init(); rep(i,p+1,n-1)block[num].ins(tmp.s[i]); block[num].pre = x; block[num].nxt = tmp.nxt; block[tmp.pre].nxt = x; block[tmp.nxt].pre = num; tmp.init(); } int fd(int x,int &sum) { sum=0; for(int i=hd;i!=-1;i=block[i].nxt) { sum+=(int)block[i].s.size(); if(sum>=x) return i; } return 0; } void pt(int x) { printf("%d block:\n",x); printf("sz: %d\n",block[x].s.size()); rep(i,0,block[x].s.size()-1)printf("%d ",block[x].s[i]);puts(""); printf("nxt: %d\n",block[x].nxt); printf("pre: %d\n",block[x].pre); } int merge(int x,int y) { if(x==y)return x; if(x==-1||y==-1||x==hd||y==hd)return x; rep(i,0,(int)block[y].s.size()-1)block[x].ins(block[y].s[i]); block[x].nxt = block[y].nxt; block[block[y].nxt].pre = x; block[y].init(); return x; } void adpt() { for(int i=hd;i!=-1;i=block[i].nxt) { if(i!=hd&&block[i].nxt!=-1&&(int)block[i].s.size()+(int)block[block[i].nxt].s.size() <= B) { i = merge(i,block[i].nxt); } } } void solve(int l,int r) { int suml=0,sumr=0; int pl = fd(l,suml); int pr = fd(r,sumr); if(l>suml-(int)block[pl].s.size()+1) split(pl,l-(suml-(int)block[pl].s.size())-1-1); if(r<sumr) split(pr,r-(sumr-(int)block[pr].s.size())-1); pl = fd(l,suml); pr = fd(r,sumr); block[block[pl].pre].nxt = block[pr].nxt; block[block[pr].nxt].pre = block[pl].pre; block[block[hd].nxt].pre = pr; block[pl].pre = hd; block[pr].nxt = block[hd].nxt; block[hd].nxt = pl; } int s[N]; int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;++i) s[i]=i+1; init(s); rep(i,1,m) {int l,r,p; scanf("%d%d",&l,&p); r = l+p-1; solve(l,r); adpt(); } int f=0; for(int i=hd;~i;i=block[i].nxt) { if(i!=hd) { for(int j=0;j<(int)block[i].s.size();++j) { if(f)printf(" ");f=1; printf("%d",block[i].s[j]); } } }puts(""); return 0; }
E. Sort String
這句話腦抽讀不懂。。。就查了下中文題意
For each i in [0,|S|-1], let Si be the substring of S starting from i-th character to the end followed by the substring of first i characters of S. Index of string starts from 0.
就是我把這個串循環移位,把相同的分在一組輸出。
做法就是kmp找循環節,如果這個串不是周期串,則每個位置都不在一組,否則就把一個周期拆開即可。難點真的在讀題。
#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)
#define pb push_back
typedef long long ll;
const int N = 1e6 + 7;
using namespace std;
int len,nxt[N];
char s[N];
void getnxt() {
int i=0,j;
j=nxt[0]=-1;
while(i<len) {
while(j!=-1&&s[i]!=s[j])j=nxt[j];
nxt[++i]=++j;
}
}
int main() {
scanf(" %s",s);
len = strlen(s);
getnxt();
int t = len - nxt[len];
if(len%t) {
printf("%d\n",len);
rep(i,0,len-1)printf("1 %d\n",i);
}
else {
printf("%d\n",t);
rep(i,0,t-1) {
printf("%d",len/t);
for(int j=i;j<len;j+=t)printf(" %d",j);puts("");
}
}
return 0;
}
H. Diff-prime Pairs
先保證i小於j,打了個表發現,對於素數j,與他配對的就是它比他小的所有素數,對於合數,與他配對的就是它的所有素因子,的配對之和。於是考慮,每個素數x對答案的貢獻,就是能整除x的數乘上比他小的素數的個數,最後乘2
#include <bits/stdc++.h>
typedef long long ll;
const int N = 10000000;
using namespace std;
ll ans=0,num=0;
int n,phi[N+1],notp[N+1],p[N+1];
void init() {
notp[1]=1;
for(int i=2; i<=n; i++) {
if(!notp[i]) p[++p[0]] = i;
for(int j=1; j<=p[0] && i*p[j]<=n; j++) {
notp[i*p[j]] = 1;
if(i%p[j] == 0) break;
}
}
}
int main() {
scanf("%d",&n);
init();
for(int i=1;i<=n;++i) if(!notp[i]){
ans += (n/i)*num;
++num;
}
printf("%lld\n",ans*2LL);
return 0;
}
I. Expected Size of Random Convex Hull
一開始讀錯題,拿pick定理搞了半天。。。因為n十分的小,於是直接隨機然後取平均。然後寫了銳角坐標系中的隨機取點。WA了之後,發現答案好像與三角形的形狀無關,感性思考一下,任何一種點的分布,都可以通過線性變換成為另一個三角坐標系中的點。所以答案與n相關,那就可以打表了。三角形直接取最簡單的沿xy軸的等腰直角三角形,讓邊長盡可能長。然後,我的寫法每種n隨機了1e8次,精度才夠。
#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)
#define pb push_back
typedef long long ll;
typedef long double LD;
const int lim = 100000005;
using namespace std;
struct point{
double x,y;
point operator + (const point a)const {
point t;
t.x = a.x+x, t.y = a.y+y;
return t;
}
point operator - (const point a)const {
point t;
t.x = x - a.x, t.y = y - a.y;
return t;
}
}p[3],v[12];
int n,cnt;
point mkp() {
point C;
C.x = ((double)rand())/RAND_MAX;
C.y = ((double)rand())/RAND_MAX;
return C;
}
struct Line_segment {
point s,e;
Line_segment() {}
Line_segment(point a,point b):s(a),e(b) {}
};
double multiply(point sp,point ep,point op) {
return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y));
}
bool cmp1(point a,point b) {
if(a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
point res[22];
int graham(point pnt[],int n,point res[]) {
sort(pnt,pnt+n,cmp1);
int m=0, k;
for(int i = 0; i < n; i++) {
while(m>1 && multiply(res[m-1],pnt[i],res[m-2])<=0) m--;
res[m++]=pnt[i];
}
k = m;
for(int i = n-2; i >= 0; i--) {
while(m>k && multiply(res[m-1],pnt[i],res[m-2])<=0) m--;
res[m++]=pnt[i];
}
if(n > 1) m--;
return m;
}
void getpoint() {
for(int i=0;i<n;++i) {
point p1 = mkp();
if(p1.x<p1.y)swap(p1.x,p1.y);
v[i]=p1;
}
}
int tubao() {
return graham(v,n,res);
}
double a[]={0,0,0,3.000000,3.6667248067,4.1667674917,4.5667089017,4.9000191650,5.1856561607,5.4356834882,5.6579956371};
int main() {
//freopen("out.txt","w",stdout);
rep(i,0,2)scanf("%lf%lf",&p[i].x,&p[i].y);
scanf("%d",&n);
// for(n=3;n<=10;++n) {
// srand(time(0));
// LD ans = 0;
// rep(ti,1,lim) {
// getpoint();
// int tmp = tubao();
// ans += tmp;
// }
// printf("%.10f\n",(double)ans/lim);
// }
printf("%.10f\n",a[n]);
return 0;
}
牛客網暑期ACM多校訓練營(第三場)