D - ABC Transform
题意:
给定一个包含ABC的字符串,对于每一次扩展,一个字符会变为两个字符。
A →BC, B →CA, C →AB.
Q次询问,扩展ti次之后,第ki个字符是什么?
Q ≤10^5, ti<10^18, ki<min(1e18, the length of S(t))
思路:
对于较高的层数来说,每次找到其来时的路径,就像一个完全二叉树,对于当前层数的第x个节点,其父节点是上一层的第x/2(上取整)个。
因为x不会超过1e18,所以照这样每次除2,不超过64次就会变为1。变为1之后,其祖先节点都会是1,因为第1个位置有循环性,所以直接取模看当前节点是什么,然后按着路径再回去。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
char a[N];
int b[N];
signed main(){
Ios;
cin>>a+1;
n = strlen(a+1);
mp['A'] = {'B', 'C'}, mp['B'] = {'C', 'A'}, mp['C'] = {'A', 'B'};
cin>>m;
while(m--)
{
int floor, cnt;
cin>>floor>>cnt;
int id;
if(floor>=60) id=1;
else
{
int x = 1ll<<floor;
id = (cnt+x-1)/x;
if(cnt%x==0) cnt=x;
else cnt%=x;
}
int t=0;
while(cnt!=1)
{
b[++t] = cnt%2;
cnt = (cnt+1)/2;
floor--;
}
char st;
if(floor%3==0) st = a[id];
if(floor%3==1){st = char(a[id]+1);if(st=='D') st='A';}
if(floor%3==2){st = char(a[id]+2);if(st=='D') st='A';if(st=='E') st='B';}
for(int i=t;i>=1;i--)
{
if(b[i]==0) st = mp[st].se;
else st = mp[st].fi;
}
cout << st << endl;
}
return 0;
}
D. Make Them Equal
题意:
给定长度为 n(1 ≤n ≤10^3) 的数组 a,初始全为 1,可以经过最多 k 次操作:
对于一个 i,选择一个 x,ai 更新为 ai+ai/x。
如果变成的 ai==bi 的话,会得到ci个金币。1 ≤bi ≤10^3,0 ≤k ≤10^6
问,最多能够得到多少金币?
思路:
dp预处理出 1 变化为 x 的最小操作数,于是就转化为 n 个物品,每个物品有价值和花费,
求花费不超过k的最大总价值。但是二重循环会超时。
最后发现最小操作数最大不会超过12,所以花费不超过13*n,这样就能过了。
dp处理:
从小到大遍历每个数,用 当前数+因数 来更新后面较大的数的状态;
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], cost[N];
int dp[1010][13010], w[N];
signed main(){
Ios;
for(int i=2;i<=1000;i++) cost[i]=1e9;
for(int i=1;i<=1000;i++)
{
for(int j=1;j<=i;j++)
{
int t = i/j;
cost[i+t] = min(cost[i+t], cost[i]+1);
}
}
cin>>T;
while(T--)
{
cin>>n>>m;
m = min(m, 13*n);
for(int i=1;i<=n;i++)
{
int x;cin>>x;
a[i] = cost[x];
}
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++) dp[i][j]=dp[i-1][j];
for(int j=a[i];j<=m;j++)
{
dp[i][j] = max(dp[i-1][j], dp[i-1][j-a[i]]+w[i]);
}
}
cout << dp[n][m] << endl;
}
return 0;
}
C. Weird Sum
题意:
求一个矩阵中,所有颜色相同的点的 哈密顿距离 之和。
思路:
哈密顿距离,可以分解成两个一维问题。
求所有相同种类点之间的距离之和,利用相邻两个点之间的小区间。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N];
vector<int> v1[N], v2[N];
signed main(){
Ios;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int x;cin>>x;
v1[x].pb(i), v2[x].pb(j);
}
int ans=0;
for(int i=1;i<=100000;i++)
{
if(v1[i].size()==0) continue;
int n = v1[i].size()-1;
for(int j=0;j<n;j++)
{
ans += (v1[i][j+1]-v1[i][j])*((j+1)*(n-j));
}
}
for(int i=1;i<=100000;i++)
{
sort(v2[i].begin(), v2[i].end());
if(v2[i].size() == 0) continue;
int n = v2[i].size()-1;
for(int j=0;j<n;j++)
{
ans += (v2[i][j+1]-v2[i][j])*((j+1)*(n-j));
}
}
cout << ans;
return 0;
}
D. Integral Array
题意:
给定 n 个数,判断是否满足下列条件:
从中挑选出两个数 x≥y(可以相同),x/y 也在这 n 个数中。
n ≤10^6, maxa ≤10^6.
思路:
遍历每个数x,枚举其倍数y,判断是否是 [y, y+x-1] 这个区间中存在集合中的数(前缀和判断),并且y/x不存在。
Code:
const int N = 2000010, mod = 1e9+7;
int T, n, m, k;
int a[N], s[N], t[N];
set<int> st;
bool mp[N];
signed main(){
Ios;
cin>>T;
while(T--)
{
cin>>n>>m;
st.clear();
for(int i=1;i<=n;i++){
cin>>t[i];
int x = t[i];
a[x]++, mp[x]=1;
st.insert(x);
}
for(int i=1;i<=m;i++) s[i] = s[i-1]+a[i];
int flag=0;
if(*st.begin() != 1) flag=1;
for(auto x:st)
{
for(int j=x;j<=m;j+=x)
{
if(s[min(j+x-1, m)] - s[j-1] > 0 && !mp[j/x]) flag=1;
}
}
for(int i=1;i<=m;i++) a[i] = mp[i] = 0;
if(flag) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}