T1
看过数据,发现只要检查好恶心的暴力就完了。
题意就是让你枚举一种攻击方案使所有人的血量的最小值为 $0space orspace 1$ 且连续(可以有人血量相同)。
枚举一下目标血量序列的最小值是 $0$ 还是 $1$,再枚举一下最大值,然后 $dfs$ 判断是否存在合法情况即可。
不过我数组开小了(正好开的 $7$),输入 $b[7]$ 的时候指针爆到了 $m$ 处,然后把输入值刷给 $m$ 了……调了半天。

1 #include<bits/stdc++.h> 2 #define N 8 3 #define M 8 4 using namespace std; 5 inline int read(){ 6 int x=0; bool f=1; char c=getchar(); 7 for(;!isdigit(c);c=getchar()) if(c=='-') f=0; 8 for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0'); 9 if(f) return x; 10 return 0-x; 11 } 12 int n,m,a1[N],a2[N],b1[M],b2[M]; 13 int i,j; 14 short vis[13]; 15 bool dfs(int cur,int cnt,int num){ 16 if(cur>m){ 17 if(cnt==j-i+1 && num==n+m) return 1; 18 else return 0; 19 } 20 if(!a2[cur]){ 21 if(dfs(cur+1,cnt,num)) return 1; 22 else return 0; 23 } 24 int hp,tmpcnt=cnt,tmpnum=num,tmp2,tmp1; 25 for(int x=1;x<=n;++x){ 26 if(!b1[x]) continue; 27 28 tmp1=max(b1[x]-a2[cur],0); 29 if(tmp1<i) continue; 30 if(--vis[b1[x]]==0 && b1[x]<=j) --cnt; 31 swap(b1[x],tmp1); 32 if(vis[b1[x]]++==0 && b1[x]<=j) ++cnt; 33 if(tmp1>j && b1[x]<=j) ++num; 34 35 tmp2=max(b2[cur]-a1[x],0); 36 if(tmp2<i) continue; 37 if(--vis[b2[cur]]==0 && b2[cur]<=j) --cnt; 38 swap(b2[cur],tmp2); 39 if(vis[b2[cur]]++==0 && b2[cur]<=j) ++cnt; 40 if(tmp2>j && b2[cur]<=j) ++num; 41 42 //printf("dfs:%d->%d %d %d %d->%d %d->%d ",cur,x,cnt,num,tmp1,b1[x],tmp2,b2[cur]); 43 if(dfs(cur+1,cnt,num)) return 1; 44 45 num=tmpnum; 46 cnt=tmpcnt; 47 --vis[b2[cur]]; 48 swap(b2[cur],tmp2); 49 ++vis[b2[cur]]; 50 --vis[b1[x]]; 51 swap(b1[x],tmp1); 52 ++vis[b1[x]]; 53 } 54 if(dfs(cur+1,cnt,num)) return 1; 55 return 0; 56 } 57 int main(){ 58 //freopen("defile.in","r",stdin); 59 //freopen("defile.out","w",stdout); 60 n=read(),m=read(); 61 int to=0; 62 for(i=1;i<=n;++i) a1[i]=read(),b1[i]=read(),to=max(to,b1[i]),vis[b1[i]]++; 63 for(i=1;i<=m;++i) a2[i]=read(),b2[i]=read(),to=max(to,b2[i]),vis[b2[i]]++; 64 for(i=0;i<=1;++i){ 65 int lj=0,_lj=0; 66 for(j=i;j<=min(i+n+m-1,to);++j){ 67 if(vis[j]) ++lj;//printf("%d %d %d %d %d ",i,j,lj,_lj+vis[j],vis[2]); 68 if(dfs(1,lj,_lj+=vis[j])){printf("Yes "); return 0;}//system("pause"); 69 } 70 } 71 printf("No "); 72 return 0; 73 } 74 /* 75 4 3 76 1 8 6 5 4 1 3 8 77 4 2 7 2 8 6 78 */
T2
$noip2017space D2T2$ 再现
什么,这状压裸题还用讲嘛?
一看 $k=15$,以每个宝藏点为起点 $bfs$ 求一下它到其它各个宝藏点的最短距离,然后设 $dp(s,i)$ 表示宝藏点挖掘情况的 $01$ 集合为 $s$,当前最后挖到第 $i$ 个宝藏点时所需的最短时间。若一个情况被转移后总用时$le T$ ,就再更新一下能在规定时间 $T$ 内挖到的最大宝藏数量。这个计算嘛,我用的就是 $noip2018$ 提高组初赛 $T10$ 的做法,直接计算挖掘情况 $s$ 中 $1$ 的数量,就是挖的宝藏数量。
时间复杂度 $O(2^k*k^2)$。

1 #include<bits/stdc++.h> 2 using namespace std; 3 inline int read(){ 4 int x=0; bool f=1; char c=getchar(); 5 for(;!isdigit(c);c=getchar()) if(c=='-') f=0; 6 for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0'); 7 if(f) return x; 8 return 0-x; 9 } 10 11 const int fx[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; 12 int n,m,k,T,dis[502][502],dp[1<<16][16],far[16][16]; 13 char c[502][502]; 14 vector<pair<int,int> >v; 15 bool vis[502][502]; 16 void bfs(int s){ 17 queue<pair<int,int> >Q; 18 Q.push(v[s]); 19 memset(dis,0x7f,sizeof dis); 20 memset(vis,0,sizeof vis); 21 dis[v[s].first][v[s].second]=0; 22 vis[v[s].first][v[s].second]=1; 23 int i,x,y,gx,gy; 24 while(!Q.empty()){ 25 x=Q.front().first, y=Q.front().second, Q.pop(); 26 for(i=0;i^4;++i){ 27 gx=x+fx[i][0], gy=y+fx[i][1]; 28 if(gx<1 || gy<1 || gx>n || gy>m || c[gx][gy]=='#' || vis[gx][gy]) continue; 29 dis[gx][gy]=dis[x][y]+1, vis[gx][gy]=1; 30 Q.push(make_pair(gx,gy)); 31 } 32 } 33 } 34 35 int ans; 36 int main(){ 37 //freopen("treasure.in","r",stdin); 38 //freopen("treasure.out","w",stdout); 39 n=read(),m=read(),k=read(),T=read(); 40 int i,j,p; 41 v.push_back(make_pair(0,0)); 42 for(i=1;i<=n;++i){ 43 scanf("%s",c[i]+1); 44 for(j=1;j<=m;++j){ 45 if(c[i][j]=='*') v.push_back(make_pair(i,j)); 46 if(c[i][j]=='S') v[0]=make_pair(i,j); 47 } 48 } 49 for(i=0;i<=k;++i){ 50 bfs(i); 51 for(j=1;j<=k;++j) far[i][j]=dis[v[j].first][v[j].second]; 52 } 53 int to=1<<k,go; 54 memset(dp,0x7f,sizeof dp); 55 for(i=1,go=1; i<=k; ++i, go<<=1){ 56 dp[go][i]=far[0][i]; 57 if(dp[go][i]<=T) ans=1; 58 } 59 for(i=1;i^to;++i){ 60 for(j=1;j<=k;++j){ 61 if(!(i & (1<<(j-1)))){ 62 go=i|(1<<(j-1)); 63 for(p=1;p<=k;++p) if(i & (1<<(p-1))) dp[go][j]=min(dp[go][j],dp[i][p]+far[j][p]); 64 //printf("(%d,%d)<-(%d,%d) %d %d %d ",go,j,i,p,dp[go][j],dp[i][p],far[j][p]); 65 if(dp[go][j]<=T){ 66 int tmp=go,zero=0; 67 while(tmp) tmp&=(tmp-1), ++zero; 68 ans=max(ans,zero); 69 //printf("newans:%d %d ",go,zero); 70 } 71 } 72 } 73 //system("pause"); 74 } 75 /* 76 for(i=0;i^to;++i){ 77 for(j=1;j<=k;++j) 78 if(!(i & (1<<(j-1)))) printf("%d ",dp[i|(1<<(j-1))][j]); 79 putchar(' '); 80 }*/ 81 printf("%d ",ans); 82 return 0; 83 } 84 /* 85 4 5 6 9 86 **... 87 *##*. 88 #**.. 89 ..S.. 90 */
T3
神仙省选级题目,我感觉我要被集训队吊着打……
20pts:
瞎 jb 搜。
60pts:
设 $f_i$ 表示前 $i$ 个音符已经划分完毕,已经划分过的最短乐章最长是多少。
易得 $f_i = max{min(f_j, i − j)}$,条件是 $[j + 1, i]$ 区间没有相同的音符。
计算方案的话可以设 $g_i$ 表示前 $i$ 个音符已经划分完毕,已经划分过的乐章长度均 $ge f_n$ ,可得 $g_i =sum_{j=0}^{i−f_n} g_j$,其中 $[j + 1, i]$ 区间没有相同的音符,条件是 $j$ 为最大的满足要求的。
至于求没有相同音符的区间 $[j + 1, i]$ ,一直向前扩展到出现一个重复字符为止就可以了,中间的 $j$ 都满足要求。
答案即为 $f_n$ 和 $g_n$ ,时间复杂度为 $O(n^2)$。
100pts:
仔细思考,会想到求没有相同音符的区间 $[j + 1, i]$ 这个东西,可以用滑动窗口直接求最大的需要转移的 $j$,因为 $i$ 往右移动,$j$ 也会往右移动或不动。
而快速取到最大 $j$ 的方法搞定后,$g$ 数组就只是简单地转移了前缀和。也就是说可以 $O(n)$ 推 $g$ 数组。
但是 $f$ 数组的递推没法优化到 $O(n^2)$ 以下,因此不能再推它了。
现在的问题成了如何直接得知 乐章长度下限 $f_n$。
我们注意到可以通过二分卡下界($mid$ 设为最短乐章长度的最小值)的方式来求出 $f_n$。
具体原因是,由于这种二分只卡下界,$f_n$ 具有单调性。如果 $f_n$ 取某一值可以,那取更大的值也可以。
于是 $g_i=sum_{j=0}^{i-mid} g_j$。
判断是否存在让每段乐章长度均$ge mid$ 的方案,直接判 $g_n$ 是否大于 $0$ 即可。
时间复杂度 $O(n*log(n))$。

1 #include<cstdio> 2 #include<cstring> 3 #include<cctype> 4 #include<cmath> 5 #include<algorithm> 6 #define rep(i,s,t) for(int i=s;i<=t;i++) 7 #define dwn(i,s,t) for(int i=s;i>=t;i--) 8 using namespace std; 9 inline int read() { 10 int x=0,f=1;char c=getchar(); 11 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 12 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; 13 return x*f; 14 } 15 typedef long long ll; 16 const int maxn=530010; 17 const int mod=998244353; 18 int n,A[maxn],f[maxn],st[maxn],last[maxn],tmp[maxn],g[maxn],sumv[maxn]; 19 int check(int lim) { 20 sumv[0]=1; 21 rep(i,1,n) { 22 int l=st[i]-1,r=i-lim; 23 if(l>r) f[i]=0; 24 else f[i]=(sumv[r]-(l>=1?sumv[l-1]:0))?1:0; 25 sumv[i]=sumv[i-1]+f[i]; 26 } 27 return f[n]; 28 } 29 int calc(int ans) { 30 g[0]=sumv[0]=1; 31 rep(i,1,n) { 32 int l=st[i]-1,r=i-ans; 33 if(l>r) g[i]=0; 34 else g[i]=(sumv[r]-(l>=1?sumv[l-1]:0)+mod)%mod; 35 sumv[i]=(sumv[i-1]+g[i])%mod; 36 } 37 return g[n]; 38 } 39 int main() { 40 //freopen("instrument.in","r",stdin); 41 //freopen("instrument.out","w",stdout); 42 n=read(); 43 rep(i,1,n) tmp[i]=A[i]=read(); 44 sort(tmp+1,tmp+n+1); 45 rep(i,1,n) A[i]=lower_bound(tmp+1,tmp+n+1,A[i])-tmp; 46 rep(i,1,n) st[i]=max(st[i-1],last[A[i]]+1),last[A[i]]=i; 47 int l=1,r=n+1,mid; 48 while(l+1<r) if(check(mid=l+r>>1)) l=mid; else r=mid; 49 printf("%d %d ",l,calc(l)); 50 return 0; 51 }