补了四题:1002,1005,1006,1011
1002:Nonsense Time
先求总的LIS,记录一个LIS。然后考虑时间倒流,每次删除一个点,如果这个点在求得的LIS里面,就暴力重新求一次LIS。因为数据随机,所以LIS期望长度是
n
\sqrt{n}
n,求一次LIS复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),总复杂度
O
(
n
n
l
o
g
n
)
O(n\sqrt{n}logn)
O(nnlogn)
神奇的一题。
#include<bits/stdc++.h>
#define P pair<int, int>
using namespace std;
const int maxn = 1e5 + 50;
int k[maxn], a[maxn];
int pre[maxn];
int vis[maxn];
int del[maxn];
vector<P> b;
int n;
int work()
{
b.clear();
for(int i = 0; i < n; ++i) vis[i] = 0;
for(int i = 0; i < n; ++i){
if(del[i]) continue;
int p = lower_bound(b.begin(),b.end(),P(a[i], i)) - b.begin();
if(p == b.size()) b.push_back(P(a[i], i));
else b[p] = P(a[i],i);
if(p == 0) pre[i] = -1;
else pre[i] = b[p-1].second;
}
if(b.size() == 0) return 0;
int p = b[b.size()-1].second;
while(p!=-1) vis[p] = 1, p = pre[p];
return b.size();
}
void init()
{
scanf("%d", &n);
memset(del,0, (n+1)<<2);
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int i = 0; i < n; ++i) scanf("%d", &k[i]), k[i]--;
}
int ans[maxn];
void sol()
{
int len = work();
for(int i = n-1; i >= 0; --i){
ans[i] = len;
del[k[i]] = 1;
if(vis[k[i]]) len = work();
}
for(int i = 0; i < n; ++i){
if(i > 0) printf(" ");
printf("%d",ans[i]);
}printf("\n");
}
int main()
{
int T;cin>>T;
while(T--){
init();sol();
}
}
1005:Snowy Smile
把每个点按x排序,然后枚举最左边的点作为左边界,然后依次按x从小到大加入点,每加入一个点,最右边可到达的边界的扩展一点。把y离散化,记录第i行加入点的总价值为Y[i],每次扩展右边界后求一下Y数组的最大子段和来更新答案。
需要维护一个可修改的最大子段和。
#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e3 + 50;
int cc[maxn];
int num, n;
struct node{
int x, y;
ll w;
bool operator < (const node& a)const{return x < a.x;}
}e[maxn];
ll sum[maxn<<2], lv[maxn<<2], rv[maxn<<2], v[maxn<<2];
void up(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
v[rt] = max(v[rt<<1], v[rt<<1|1]);
v[rt] = max(v[rt], rv[rt<<1]+lv[rt<<1|1]);
lv[rt] = max(lv[rt<<1], sum[rt<<1] + lv[rt<<1|1]);
rv[rt] = max(rv[rt<<1|1], rv[rt<<1] + sum[rt<<1|1]);
return;
}
void build(int rt, int l, int r){
sum[rt] = lv[rt] = rv[rt] = v[rt] = 0;
if(l == r) return;
build(lson); build(rson);
}
void add(int rt, int l, int r, int id, ll w){
if(l == r){
sum[rt] += w;
v[rt] = lv[rt] = rv[rt] = sum[rt];
return;
}
if(id <= mid) add(lson, id, w);
else add(rson, id, w);
up(rt);
}
void init()
{
scanf("%d", &n);
num = 0;
for(int i = 0; i < n; ++i){
scanf("%d%d%lld", &e[i].x, &e[i].y, &e[i].w);
cc[++num] = e[i].y;
}
sort(cc+1,cc+1+num); num = unique(cc+1,cc+1+num)- cc-1;
sort(e, e+n);
for(int i = 0; i < n; ++i) e[i].y = lower_bound(cc+1,cc+1+num,e[i].y) - cc;
e[n].x = 0x3f3f3f3f;
}
ll work(int id){//×ó±ß½ç
ll res = 0;
build(1, 1, num);
for(int i = id; i < n; ++i){
add(1, 1, num, e[i].y, e[i].w);
if(e[i].x != e[i+1].x){
res = max(res, v[1]);
}
}return res;
}
void sol()
{
ll ans = 0;
for(int i = 0; i < n; ++i){
if(i!=0 && e[i].x == e[i-1].x) continue;
ans = max(ans, work(i));
}
printf("%lld\n", ans);
}
int main()
{
int T;cin>>T;
while(T--){
init();
sol();
}
}
1006:Faraway
n个点一共把平面分割成了n^2个部分,对于每一个部分,枚举xe%60和ye%60的值,判断可不可行,如果可行,这个部分内有几个满足的(xe,ye)。最后把每个部分的答案加起来。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n, m;
struct node{
int x, y, k, t;
}e[15];
int a[15], ca;
int b[15], cb;
void init()
{
scanf("%d%d", &n, &m);
a[1] = m+1, b[1] = m+1;
ca = cb = 1;
for(int i = 0; i < n; ++i)
scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].k, &e[i].t), a[++ca] = e[i].x, b[++cb] = e[i].y;
sort(a, a+ca+1);
sort(b, b+cb+1);
}
bool check(int x, int y)
{
for(int i = 0; i < n; ++i){
if((abs(x-e[i].x) + abs(y-e[i].y))%e[i].k != e[i].t) return false;
}
return true;
}
ll cal(int l, int r){
if(r-l <= 0) return 0;
return (r-l-1)/60 + 1;
}
void sol()
{
ll ans = 0;
for(int i = 0; i < ca; ++i){
for(int j = 0; j < cb; ++j){
for(int x = 0; x < 60; ++x){
if(x < a[i+1])
for(int y = 0; y < 60; ++y){
if(y < b[j+1]){
if( check(a[i]+x, b[j]+y) )
ans = ans + cal(a[i]+x, a[i+1])*cal(b[j]+y, b[j+1]);
}
}
}
}
}
printf("%lld\n", ans);
}
int main()
{
int T;cin>>T;
while(T--){
init();sol();
}
}
1011:11 Dimensions
没能get到题解的科学写法,不过受到dls的启发写了一个看上去就很对的写法。
因为只考虑32个问号就可以构成
1
0
32
10^{32}
1032个数字了(这数量得用__int128存),所以我们有理由相信,如果在这
1
0
32
10^{32}
1032个数字里面都没找到第k个满足%m等于0的那个数字,那么它就无解。(因为k<=1e18)。
所以我们只找最后32个‘?’,而其他的‘?’都当成0处理。用dp[i][j]表示填了后i+1个问号,模m等于j的不同填法的数目。对于每个查询,只要dfs到底部即可找到答案。
#include<bits/stdc++.h>
#define ll long long
#define P pair<ll, int>
using namespace std;
const int maxn = 1e5 + 50;
const ll mod = 1e9 + 7;
__int128 dp[55][21];
char s[maxn];
int n, m, Q;
ll bin[maxn];//%m
ll pp[maxn];//%mod
int id[55], cnt;
ll Ans, k;
__int128 dfs(int pos, ll res, __int128 sum, ll val){//位置,需要的余数,当前计数值,前面累计值
// cout<<"p:"<<pos<<" res:"<<res<<" sum:"<<sum<<endl;
// cout<<"Ans:"<<Ans<<endl;
if(pos == -1){//搜到了底部
if(sum + 1 == k && res == 0){
Ans = val;
return 1;
}
else if(res == 0) return 1;
else return 0;
}
if(dp[pos][res] != -1){
if(Ans != -1) return dp[pos][res];
if(sum + dp[pos][res] < k) return dp[pos][res];
}
__int128 ans = 0;
for(ll x = 0; x <= 9; ++x){
ans += dfs(pos-1, ((res-x*bin[n-1-id[pos] ])%m + m)%m, sum+ans, (val + x*pp[n-1-(id[pos])])%mod);
if(dp[pos][res]!=-1 && Ans != -1) return dp[pos][res];
}
dp[pos][res] = ans;
return dp[pos][res];
}
void sol(ll sum ,ll res)//当前总值,当前余数
{
//cout<<"sum:"<<sum<<" res:"<<res<<endl;
while(Q--){
scanf("%lld", &k);
Ans = -1;
dfs(cnt-1, (m-res)%m, 0, sum);
printf("%lld\n", Ans);
}
}
int main()
{
//freopen("1011.in", "r", stdin);
int T;cin>>T;
while(T--){
scanf("%d%d%d", &n, &m, &Q);
scanf("%s", s);
bin[0] = 1, pp[0] = 1;
for(int i = 1; i <= n; ++i) bin[i] = bin[i-1]*10%m, pp[i] = pp[i-1]*10%mod;
int pos = n-1;
cnt = 0;
ll sum = 0, res = 0;
while(pos >= 0 && cnt < 32){
if(s[pos] != '?') {
int x = s[pos]-'0';
sum = (sum + pp[n-1-pos]*x)%mod;
res = (res + bin[n-1-pos]*x)%m;
}
else {
id[cnt++] = pos;
}
pos--;
}
for(int i = 0; i <= pos; ++i) {
if(s[i] == '?') continue;
sum = (sum + pp[n-1-i]*(s[i] - '0'))%mod;
res = (res + bin[n-1-i]*(s[i]-'0'))%m;
}
for(int i = 0; i < cnt; ++i) for(int j = 0; j < m; ++j) dp[i][j] = -1;
sol(sum, res);
}
}