A. Knight Tournament
problem
输入 n m 和 m 行 每行 l r x 表示把区间[l, r]中除了x还没有值的变成x
问最后每个n的值分别是多少,没有就是0
think
线段树
code
int val[333333<<2];
void update(int l, int r, int k, int L, int R, int x){
if(L > R || val[k] > 0) return;
if(l==L && r==R){
val[k] = x;
return;
}
int mid = (l+r) >> 1;
if(R <= mid) update(l, mid, k<<1, L, R, x);
else if(L > mid) update(mid+1, r, k<<1|1, L, R, x);
else{
update(l, mid, k<<1, L, mid, x);
update(mid+1, r, k<<1|1, mid+1, R, x);
}
}
int query(int l, int r, int k, int p, int ans){
if(l==r){
return val[k] == 0 ? ans : val[k];
}
if(val[k] > 0) ans = val[k];
int mid = (l+r) >> 1;
if(p <= mid) return query(l, mid, k<<1, p, ans);
else return query(mid+1, r, k<<1|1, p, ans);
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
int l, r, x;
for(int i = 0; i < m; ++i){
scanf("%d%d%d", &l, &r, &x);
update(1, n, 1, l, x-1, x);
update(1, n, 1, x+1, r, x);
}
for(int i = 1; i <= n; ++i){
printf("%d ", query(1, n, 1, i, 0));
}
return 0;
}
B. Xenia and Hamming
problem
输入n m 和两个串x y,n*|x| 和 m*|y|一定相等n m 1e12; |x| |y| 1e6
问n个x组成的串 和 m个y组成的串 对应位置不一样的有多少个
think
看成一样的有多少个了。于是就求的一样的,然后减。
求最小公倍数长度的串的一样数,再乘以倍数。
看最小公倍数,画一画就很明显。记录每个字母在每个最大公约数的余数位置的个数。
code
const int N = 1111111;
char x[N], y[N];
LL dp[N][26];
LL gcd(LL a, LL b){
return (a==0 ? b : gcd(b%a, a));
}
int main(){
LL n, m;
scanf("%I64d%I64d%s%s", &n, &m, x, y);
LL xx = (LL)strlen(x);
LL yy = (LL)strlen(y);
LL g = gcd(xx, yy);
for(int i = 0; i < yy; ++i){
++dp[i%g][y[i]-'a'];
}
LL ans = 0;
for(int i = 0; i < xx; ++i){
ans += dp[i%g][x[i]-'a' ];
}
ans *= n*g/yy;
printf("%I64d\n", n*xx-ans);
return 0;
}
C. Compartments
problem
n个车厢,每个车厢最多4个人,给你每个车厢有多少我们的人,问最少我们有几个人和别人换票,才能使得每个车厢有0或3或4个我们的人。
think
构造。
code
int a[5];
int solve(int a0, int a1, int a2, int a3, int a4, int n3, int n4){
if(a3 + a4 >= n3 + n4) return a1 + a2 * 2;
else if(a2 + a3 + a4 >= n3 + n4) return a1 + (a2 + a3 + a4 - n3 - n4) * 2 + ((a4 - n4) > 0 ? (a4 - n4) : 0);
else return (a1 + a2 + a3 + a4 - n3 - n4) + ((a4 - n4) > 0 ? (a4 - n4) : 0);
}
int main() {
int n, sum = 0, x;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d", &x);
++a[x];
sum += x;
}
if(sum==1 || sum==2 || sum==5){
puts("-1");
return 0;
}
int ans = sum;
for(int i = 0, k = 1; 4 * i <= sum; i += k){
if((sum - 4 * i) % 3){
continue;
}
else k = 3;
int j = (sum - 4 * i) / 3;
if(i + j > n) continue;
int tmp = solve(a[0], a[1], a[2], a[3], a[4], j, i);
ans = min(ans, tmp);
}
cout<<ans<<endl;
return 0;
}
D. Bags and Coins
problem
n个袋子m个硬币,给你每个袋子里面的硬币数,求一种方法满足条件并且每个硬币都不在外面
输出n行,每一行输出直接里面有几个硬币,直接里面有几个袋子,这几个袋子分别是第几个袋子
think
关键就是找到包含最大的那个袋子的和正好是m的方案
除了最大那个袋子,与他和是m的那个方案其他的都标记为single表示他不在别的袋子里面,别的袋子也不在他里面。
这个我用了背包,会超市, 特判最大是1的情况。
code
int dp[N], pre[N], single[N];
int aa[N];
struct point{
int a, id;
}p[N];
int bef[N];
int n, m, mx;
LL sum;
bool cmp(point x, point y){
return x.a < y.a;
}
void out(){
for(int i = m; i > 0; ){
int j = pre[i];
i -= p[j].a;
if(j == 0) break;
single[p[j].id] = 1;
}
for(int i = 1; i <= n; ++i){
if(single[i]){
printf("%d %d\n", aa[i], 0);
}
else {
int tmp1 = 0, tmp2, ii = bef[i];
while(ii && single[ii]) ii = bef[ii];
if(ii > 0) printf("%d %d %d\n",aa[i] - aa[ii], 1, ii);
else printf("%d %d\n",aa[i], 0);
}
}
}
void solve(){
if(p[n].a == 1){
for(int i = 1; i <= m; ++i) printf("%d %d\n", 1, 0);
for(int i = m + 1; i <= n; ++i) printf("%d %d %d\n", 0, 1, i-1);
return;
}
dp[p[n].a] = 1;
if(p[n].a == m) {
out();
return;
}
int last = p[n].a;
for(int i = n-1; i >= 1; --i){
for(int j = m - p[i].a; j >= last; --j){
if(dp[j]==0 || dp[j+p[i].a]==1) continue;
pre[j+p[i].a] = i;
dp[j+p[i].a] = 1;
if(j+p[i].a==m){
out();
return;
}
}
}
puts("-1");
}
int main (){
scanf("%d%d", &n, &m);
sum = mx = 0;
for(int i = 1; i <= n; ++i){
scanf("%d", &p[i].a);
aa[i] = p[i].a;
p[i].id = i;
mx = max(mx, p[i].a);
sum += (LL)aa[i];
}
if(mx > m || sum < m){
puts("-1");
return 0;
}
sort(p + 1, p + n + 1, cmp);
bef[p[1].id] = 0;
for(int i = 2; i <= n; ++i) bef[p[i].id] = p[i-1].id;
solve();
return 0;
}