B. Light bulbs
题意:
一排 N 个初始关着的灯泡,M个操作,每个操作使得区间【L,R】的状态反转,求最后开着灯泡的数量
分析:
读完题首先想到的是差分,但是T*N有点大,而M表较小,于是考虑离散化区间操作的L,R+1,然后差分就行了;注意差分后每个点代表了原序列的一个区间
代码:
#include <bits/stdc++.h>
#define l first
#define r second
#define pii pair<int,int>
using namespace std;
typedef long long LL;
const int maxn = 2e3+15;
int T,n,m,c[maxn],cnt,a[maxn];
pii p[maxn];
inline int getid(int x){
return lower_bound(c,c+cnt,x)-c;
}
int main(){
cin >> T;
for(int Case = 1; Case<= T; ++Case){
scanf("%d %d",&n,&m); cnt = 0;
memset(a,0,sizeof(a));
for(int i = 0;i < m; ++i){
scanf("%d %d",&p[i].l,&p[i].r);
c[cnt++] = p[i].l;
c[cnt++] = p[i].r+1; //差分r+1
p[i].r++;
}
sort(c,c+cnt); cnt = unique(c,c+cnt)-c;
for(int i = 0;i < m; ++i){
p[i].l = getid(p[i].l);
p[i].r = getid(p[i].r);
a[p[i].l]++; a[p[i].r]--;
}
for(int i = 1;i < maxn; ++i) a[i] += a[i-1];
int ans = 0;
for(int i = 0;i < maxn; ++i){
if(a[i]&1) ans += c[i+1]-c[i];
}
printf("Case #%d: %d\n",Case,ans);
}
return 0;
}
F. Rhyme scheme
题意:
咕咕咕
分析:
首先每一位的字母都是唯一的,那么就可以枚举每一位选什么;当然不能直接暴力出所有情况,比如枚举到第二位选A后,只需要判断后面一共有多少种情况即可,设后面一共有 x 种,如果x >= k,那么这一位选A就是合法的,否则 k -= x,继续枚举这一位选B,所以要预处理出每一位后面一共有多少情况;每一位的选择又受前面最大值的影响,类似数位dp的写法预处理即可
注意 k 的值最大会爆unsigned long long,使用__int128就好了
代码:
#include <bits/stdc++.h>
using namespace std;
typedef __int128 LL;
const int maxn = 30;
LL dp[maxn][maxn],k;
int T,n,a[maxn];
inline LL read() {
LL x = 0, f = 1;
char c = getchar();
for (; !isdigit(c);c = getchar()) if (c == '-') f = -1;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
return x * f;
}
LL dfs1(int pos,int limit){
if(pos == n+1) return 1;
if(~dp[pos][limit]) return dp[pos][limit];
LL sum = 0;
for(int i = 0;i <= limit; ++i){
sum += dfs1(pos+1,max(limit,i+1));
}
dp[pos][limit] = sum;
return sum;
}
void dfs2(int pos,int limit,LL k){
if(pos == n+1){
for(int i = 1;i <= n; ++i) putchar(a[i]+'A');
puts("");
return ;
}
for(int i = 0;i <= limit; ++i){
int p = max(limit,i+1);
if(dp[pos+1][p] >= k){
a[pos] = i; //标记第pos位选什么
dfs2(pos+1,p,k);
break;
}
else k -= dp[pos+1][p];
}
}
int main(){
cin >> T;
for(int Case = 1;Case <= T; ++Case){
cin >> n; k = read();
for(int i = 0;i < maxn; ++i){
for(int j = 0;j < maxn; ++j)
dp[i][j] = -1;
}
for(int i = 0;i < 30; ++i) dp[n+1][i] = 1;
dfs1(1,0);
printf("Case #%d: ",Case); dfs2(1,0,k);
}
return 0;
}
J. Stone game
题意:
咕咕咕
分析:
设第一个集合的和:sum1,第二个集合的和:sum2,那么需满足 sum1 >= sum2 ,并且集合1中的最小值 >= abs(sum1-sum2),自然想到枚举第一个集合的最小值,然后再求剩下的划分方案;先从小到大排序,定义 dp[x][y] 表示第 x 个数到最后一个数能组成和为 y 的方案数,倒着dp即可;假设最小值为 a[x],那么第一个集合的和只可能是第 x 个到最后一个数组成的,正符合倒着dp,然后枚举第一个集合的和判断是否满足条件即可统计答案
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 305;
const int maxm = 150100;
int dp[maxn][maxm],a[maxn],n,T,sum;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n; ++i) scanf("%d",a+i),sum += a[i];
sort(a+1,a+n+1);
for(int i = 0;i <= sum; ++i) dp[n+1][i] = 0;
dp[n+1][0] = 1; int ans = 0;
for(int i = n;i > 0; --i){
for(int j = 0;j <= sum; ++j){
dp[i][j] = dp[i+1][j];
if(j >= a[i]){ //枚举第一个集合选a[i]为最小值,并且和为j
dp[i][j] += dp[i+1][j-a[i]];
if(j>=sum-j&&j-(sum-j)<=a[i]) ans += dp[i+1][j-a[i]];
if(ans >= mod) ans -= mod;
}
if(dp[i][j] >= mod) dp[i][j] -= mod;
}
}
printf("%d\n",ans);
}
return 0;
}
G. Substring
题意:
咕咕咕
赛后补题:
大致做法就是暴力+hash,暴力是指将长度一样的匹配串一起算;暴力一遍原串(最多sqrt(1e5)次),那么就能得到原串中所有这个长度的子串,当然不能暴力的与匹配串比较,而是将子串 hash 后存入 map,就能 O(1) 得到与每个匹配串配对个数;由于两端必须一样,那么可以考虑将其作为 map 的下标,中间的字符可以乱序,所以不能用进制 hash,给每个字母对应一个质数,加法hash 即可;由于空间的限制,每次只用存对答案有贡献的值就好了
#include <bits/stdc++.h>
#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int maxn = 2e4+24;
LL a[26]={34183,13513,152993,13591,19687,350869,111187,766091,769297,633469,752273,298651,617191,880421,136067,1408397,726899,458921,2133701,2599847,2730947,4696343,10267237,18941059,34078909,69208409};
int T,m,ans[maxn];
string s,t;
struct pii{
int len,id,L,R;LL val;
}p[maxn];
LL gethash(string s){
LL res = 0;
for(int i = 1;i < sz(s)-1; ++i) res+=a[s[i]-'a'];
return res;
}
bool cmp(pii a,pii b){
return a.len < b.len;
}
unordered_map<LL,int> mp[26][26];
void solve(int len){
LL res = 0;
for(int i = 1;i < len-1; ++i) res+=a[s[i]-'a'];
if(mp[s[0]-'a'][s[len-1]-'a'].count(res))
mp[s[0]-'a'][s[len-1]-'a'][res]++;
for(int i = len;i < sz(s); ++i){
res = res-a[s[i-len+1]-'a']+a[s[i-1]-'a'];
if(mp[s[i-len+1]-'a'][s[i]-'a'].count(res))
mp[s[i-len+1]-'a'][s[i]-'a'][res]++;
}
}
int main(){
scanf("%d",&T);
while(T--){
cin >> s >> m;
for(int i = 0;i < 26; ++i)
for(int j = 0;j < 26; ++j)
mp[i][j].clear();
for(int i = 0;i < m; ++i){
cin >> t;
p[i] = (pii){sz(t),i,t[0]-'a',t[sz(t)-1]-'a',gethash(t)};
mp[p[i].L][p[i].R][p[i].val] = 0;
}
sort(p,p+m,cmp); int per = 0;
for(int i = 0;i < m; ++i){
if(p[i].len != per) solve(p[i].len);
per = p[i].len;
ans[p[i].id] = mp[p[i].L][p[i].R][p[i].val];
}
for(int i = 0;i < m; ++i) printf("%d\n",ans[i]);
}
return 0;
}