POI题目选做

POI题目选做

[POI2015]WIL-Wilcze doły

题意

给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0。请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p

题解

显然长度为D时只会更优,尺取法枚举,单调队列维护被删除的一段

 

#include<bits/stdc++.h>

using namespace std;

#define LL long long

inline LL read()
{
    LL f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 2000000 + 5;

LL n,p,d;
LL sum[MAXN],a[MAXN];
LL ans;
LL q[MAXN],head = 1,tail;

inline LL calc(LL x)
{
    return sum[x] - sum[x-d];    
} 

int main()
{
    n = read(),p = read(),d = read();
    for(int i=1;i<=n;i++) a[i] = read(),sum[i] = sum[i-1] + a[i];
    ans = d;
    q[++tail] = d;
    int l = 1;
    for(int i=d+1;i<=n;i++)
    {
        while(head <= tail && calc(i) > calc(q[tail])) tail--;
        q[++tail] = i;
        while(head <= tail && sum[i] - sum[l-1] - calc(q[head]) > p) 
        {
            l++;
            while(head <= tail && l >q[head] - d + 1) head++;    
        }
        ans = max(ans, 1LL * i - l + 1);  
     } 
     cout << ans << endl;
}
View Code

 

 

[POI2012]LIT-Letters

题意

给出两个长度相同的的只含大写字母的字符串 a,b

每次可以交换相邻字符,求a到b的最小交换次数

题解

火柴排队弱化版?

显然逆序对

 

#include<bits/stdc++.h>

using namespace std;

#define LL long long

inline LL read()
{
    LL f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 1000000 + 5;

int n;
char s[MAXN];
int a[MAXN];
queue<int>Q[30];
LL c[MAXN];
inline int lowbit(int x)
{
    return x&(-x);
}

inline LL Query(LL x)
{
    LL res = 0;
    while(x)
    {
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}

inline void update(int x,int v)
{
    while(x <= n)
    {
        c[x] += v;
        x += lowbit(x);
    }
}

int main()
{
    n = read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) a[i] = s[i]  - 'A';
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) Q[s[i] -'A'].push(i);
    for(int i=1,cur;i<=n;i++) cur = a[i],a[i] = Q[cur].front(),Q[cur].pop();
    LL ans = 0;
    for(int i=1;i<=n;i++)
    {
        ans += i - 1 - Query(a[i]);
        update(a[i],1);
    }
    cout << ans << endl;
}
View Code

 

[POI2013]LUK-Triumphal arch

题意

给一颗树,1号节点已经被染黑,其余是白的,两个人轮流操作,一开始B在1号节点,A选择k个点染黑,然后B走一步,如果B能走到A没染的节点则B胜,否则当A染完全部的点时,A胜。

求能让A获胜的最小的k

题解

显然二分答案

考虑树形DP检验

显然只需要考虑再子树i中最多需要染几次色

把儿子不够的染色次数合并上来即可

#include<bits/stdc++.h>

using namespace std;

#define LL long long

inline LL read()
{
    LL f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'||ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 3e5 + 10;

int n,ans;
vector<int>G[MAXN];
int dp[MAXN];

inline void dfs(int x,int f,int mid)
{
    int sz = 0;
    for(int i=0;i<G[x].size();i++)
    {
        int v = G[x][i];
        if(v == f) continue;
        dfs(v,x,mid);
        sz++;
        dp[x] += dp[v];        
    }
    dp[x] = max(0,dp[x]+sz-mid);
}

inline bool check(int mid)
{
    memset(dp,0,sizeof(dp));
    dfs(1,0,mid);
    if(dp[1] == 0) return true;
    else return false;
}

int main()
{
    n = read();
    for(int i=1;i<n;i++)
    {
        int u = read(),v =read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int l = 0,r = n;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(check(mid))
        {
            r = mid - 1;
            ans = mid;
        }
        else l = mid + 1;
    }
    cout << ans << endl;
}
View Code

 

posted @ 2020-11-19 21:34  wlzs1432  阅读(181)  评论(2编辑  收藏  举报