A Simple Task

来源:CF11D, 2200

状压 DP.  

令 $\mathrm{f[i][x][y]}$ 表示加入的点为 $\mathrm{i}$, 起点为 $\mathrm{x}$, 终点为 $\mathrm{y}$ 的方案.  

但是我们可以强制钦定让环中编号最小的点为起点,可以省掉一个 $\mathrm{i}$.  

然后转移的时候只可以向末断加点,不可以改变起点,否则会算重.  

最后我们对于一个环其实从两个方向都算了一遍,所以要除以 2.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int n,m,num[1<<20],is[N][N],LG[1<<N];          
ll f[1<<20][20]; 
int lowbit(int x) {
    return x & (-x); 
}
int main() {
    // setIO("input");  
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=m;++i) {
        int x,y; 
        scanf("%d%d",&x,&y); 
        is[x][y] = is[y][x] =1 ; 
    }
    for(int i=1;i<(1<<n);++i) {
        num[i] = num[i - lowbit(i)] + 1;  
    }
    for(int i=0;i<n;++i) LG[1 << i] = i; 
    ll ans = 0ll;  
    for(int i=1;i<(1<<n);++i) {
        for(int j=0;j<n;++j) {
            if((i&(1<<j)) == 0) continue; 
            if(num[i] == 1) {
                f[i][j + 1] = 1; 
            }
            if(num[i] > 2 && is[LG[lowbit(i)] + 1][j + 1]) {
                ans += f[i][j + 1];  
            }
            if(!f[i][j + 1]) continue;    
            for(int nex=0;nex<n;++nex) {
                if((1 << nex) < lowbit(i)) continue; 
                if(i & (1 << nex)) continue;  
                // 新的,且不能是最小值.  
                if(is[j + 1][nex + 1]) 
                    f[i + (1 << nex)][nex + 1] += f[i][j + 1];    
            }
        }
    }
    printf("%lld\n", ans / 2); 
    return 0; 
}

  

Product Sum

来源:CF631E, 2600   

先求出不进行操作的答案.  

每次考虑 $\mathrm{(i,j)}$ 互换的贡献.  

然后退一下式子发现这个贡献是 $\mathrm{a[i]}$ 为 $\mathrm{x}$ 的 $\mathrm{j}$ 提供系数的直线.    

这个直线直接用李超线段树维护即可.    

由于 $\mathrm{a[i]}$ 的值有负数, 所以在构建线段树时的左端点要注意一下.

#include <cstdio>
#include <vector>
#include <set>
#include <cstring>
#include <algorithm>
#define N  200009 
#define M  2000002 
#define ll long long 
#define pb push_back
#define ls now<<1
#define rs now<<1|1
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
int a[N], tree[M<<2], n; 
ll sum[N], suf[N]; 
struct Line { 
    ll k, b;   
    Line(ll k=0,ll b=0):k(k),b(b){}  
}line[N];       
ll calc(Line o, ll pos) {
    return o.k * pos + o.b; 
}
void update(int l,int r,int now,int L,int R,int id) {
    if(l>=L&&r<=R) {
        int mid = (l + r) >> 1;   
        if(calc(line[tree[now]], mid) < calc(line[id], mid))  swap(id, tree[now]);  
        // 向下传递的是 id.  
        if(l == r) return ;   
        if(calc(line[id], l) > calc(line[tree[now]], l)) 
            update(l, mid, ls, L, R, id); 
        if(calc(line[id], r) > calc(line[tree[now]], r))   
            update(mid + 1, r, rs, L, R, id); 
        return ; 
    }
    int mid=(l+r)>>1; 
    if(L<=mid)  update(l,mid,ls,L,R,id);  
    if(R>mid)   update(mid+1,r,rs,L,R,id);    
}
void build(int l,int r,int now) {
    tree[now] = 1;  
    if(l == r) return ; 
    int mid = (l + r) >> 1; 
    build(l, mid, ls); 
    build(mid + 1, r, rs);  
}
ll query(int l, int r, int now, int p) {
    if(l == r) {
        return calc(line[tree[now]], p);   
    }
    int mid = (l + r) >> 1;  
    ll cur = calc(line[tree[now]], p);   
    if(p <= mid) return max(cur, query(l, mid, ls, p)); 
    else return max(cur, query(mid + 1, r, rs, p));  
}
void build2(int l, int r, int now) {
    tree[now] = n ; 
    if(l == r) return ; 
    int mid=(l+r)>>1; 
    build2(l, mid, ls); 
    build2(mid + 1, r, rs); 
}
int main() {
    // setIO("input");  
    scanf("%d",&n); 
    int len = 1000000; 
    // printf("%d\n", n); 
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);     
    }
    sum[0] = 0; 
    for(int i=1;i<=n;++i) {
        sum[i] = sum[i - 1] + a[i]; 
        line[i].k = i; 
        line[i].b = -sum[i - 1];
    } 
    build(-len, len, 1); 
    ll fin = 0ll, ans; 
    for(int i = 1; i <= n ; ++ i) {
        fin += 1ll * i * a[i];   
    }
    ans = fin; 
    for(int i = 2; i <= n ; ++ i) {
        ll det = sum[i - 1] - 1ll * i * a[i] + query(-len, len, 1, a[i]);  
        ans = max(ans, det + fin); 
        update(-len, len, 1, -len, len, i);  
    }
    for(int i = n; i >= 1; -- i) {
        suf[i] = suf[i + 1] + a[i];   
        line[i].k = i;       
        line[i].b = suf[i + 1]; 
    }
    build2(-len, len, 1); 
    for(int i = n - 1; i >= 1; -- i) {
        ll det = -suf[i + 1] - 1ll * i * a[i] + query(-len, len, 1, a[i]); 
        ans = max(ans, fin + det);  
        update(-len, len, 1, -len, len, i);   
    }
    printf("%lld\n", ans); 
    return 0; 
}

  

Integer Game

来源:CF1375F, 2600

神仙交互,不看题解根本想不出来.  

不妨考虑先手必胜要有怎样局面:   

当前 $3$ 个数字构成等差数列且后手在上一轮对当前最大值位置操作了.   

不妨在最开始的时候让后手对任意位置加上 $\mathrm{inf}$, 设为 $\mathrm{c}$.  

然后第二步的时候只能对于更小的 $\mathrm{a,b}$ 操作了.  

这个时候令 $\mathrm{k=2c-a-b}$.   

加上之后第二次操作的数字就成了最大值,且相邻两项差为 $\mathrm{c-b}$.  

这样,在 $3$ 步之内先手就取得了胜利.   

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long 
using namespace std; 
ll a[4];  
int x[4];  
void print(ll x) {
    printf("%lld\n", x);
    printf("\n");   
    fflush(stdout); 
}
int rd() {
    ll o; 
    scanf("%lld", &o); 
    return (int)o; 
}
int main() {
    for(int i=1;i<=3;++i) {
        scanf("%lld",&a[i]); 
    }
    puts("First"); 
    ll M = 100000000000ll;
    print(M);
    x[1] = rd();     
    // scanf("%lld",&x[1]);   
    a[x[1]] += M;    
    // 下一步:2c - a - b    
    ll d2 = a[x[1]] * 3 - a[1] - a[2] - a[3];    
    print(d2);
    x[2] = rd();      
    // scanf("%d",&x[2]);    
    a[x[2]] += d2;    
    x[3] = 6 - x[1] - x[2];     
    // x[1]: 最大的.  
    // x[2]: 第二大的.       
    // x[3]: 当前最小.   
    //print(a[x[2]] - a[x[3]]);       
    printf("%lld\n", a[x[1]] - a[x[3]]); 
    return 0;    
}

  

Arpa’s overnight party and Mehrdad’s silent entering

来源:CF741C, 2600   

直接做没什么思路,看了下题解发现这是个图论题.   

显然,对于情侣的配色就是个二分图染色,然后对于其他人也同理.   

不妨将 $\mathrm{(2i, 2i-1)}$ 看成一对,然后一对之间连边.    

要判断这种连边方式是否有解.  

不难发现对于每一个点,最多连 2 条边,所以原图会构成一些链和环.  

对于每一种环的情况,也一定是偶数个点,那么就不存在奇环了.    

直接连边然后二分图染色就行了.    

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define N  2000009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int n , ans[N], vis[N], A[N], B[N]; 
vector<int>G[N]; 
void dfs(int x) {
    vis[x] = 1;  
    for(int i = 0; i < G[x].size() ; ++ i) {
        int v = G[x][i];  
        if(!vis[v]) {
            ans[v] = ans[x] ^ 1;  
            dfs(v); 
        }
    }
}
int main() {
    // setIO("input"); 
    scanf("%d",&n); 
    for(int i=1;i<=n;++i) {
        G[2 * i - 1].pb(2 * i);       
        G[2 * i].pb(2 * i - 1); 
    }
    for(int i=1;i<=n;++i) {
        int x, y; 
        scanf("%d%d",&x, &y);   
        G[x].pb(y); 
        G[y].pb(x); 
        A[i] = x;  
        B[i] = y;  
    }
    for(int i = 1; i <= 2 * n ; ++ i) {
        if(!vis[i]) dfs(i);   
    }
    for(int i = 1; i <= n ; ++ i) {
        printf("%d %d\n", ans[A[i]] + 1, ans[B[i]] + 1); 
    }
    return 0; 
}

  

Sonya and Problem Wihtout a Legend

来源:CF713C, 2300

直接弄这个严格递增序列不好搞,不妨考虑严格递增需要满足的条件.  

发现,条件为 $\mathrm{i-j\leqslant a[i]-a[j]}$ 对任意 $\mathrm{i,j}$ 都成立.  

然后化简一下就是 $\mathrm{a[j]-j \leqslant a[i]-i}$.     

令 $\mathrm{c[i]}$ 表示化简后的结果,那只需令 $\mathrm{c[i]}$ 不减就行.    

由不减的性质,序列中数字种类肯定不会变.

所以可以设 $\mathrm{f[i][j]}$ 表示前 $\mathrm{i}$ 个数,最后为 $\mathrm{j}$ 的修改次数.  

这个东西用 $\mathrm{DP}$ 求一下就好了.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 3009 
#define ll long long
#define pb push_back  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int n , cnt ; 
int a[N], c[N], A[N];  
ll f[N][N];  
int main() {
    // setIO("input"); 
    scanf("%d",&n); 
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);  
        c[i] = a[i] - i;  
    }
    cnt = n ;  
    c[++ cnt] = -N;   
    for(int i=1;i<=cnt;++i) A[i]=c[i]; 
    sort(A+1,A+1+cnt);  
    for(int i=1;i<=cnt;++i) c[i]=lower_bound(A+1,A+1+cnt,c[i])-A;  
    memset(f, 0x3f, sizeof(f));           
    f[0][1] = 0;  
    ll fin = 1000000000000000ll;  
    for(int i=1;i<=n;++i) {
        ll mi = f[i - 1][1];  
        for(int j=1;j<=cnt;++j) {
            mi = min(mi, f[i - 1][j]);     
            f[i][j] = abs(A[c[i]] - A[j]) + mi;   
            if(i == n) fin = min(fin, f[i][j]); 
        }
    }
    printf("%lld\n", fin); 
    return 0; 
}

  

Make k Equal 

来源:CF1328F, 2200

思考最后的形式:   

设答案为 $\mathrm{fin}$, 需要大于 $\mathrm{fin}$ 的都变为 $\mathrm{fin+1}$, 然后从中挑选不够的补齐.  

对于小于 $\mathrm{fin}$ 的情况同理.  

显然,答案只可能为序列中原数,或者相差值为 $1$ 的数字.    

直接枚举答案然后利用前缀和算一算就好了.   

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <map>
#define N  600009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n,K,cnt; 
ll suf[N],pre[N];
int a[N],A[N],B[N];   
map<int,int>co; 
int get_pre(int pos) {
	// 求 <= pos 的个数.  
	int p = lower_bound(B + 1, B + 1 + 1 + n, pos + 1) - B;     
	return p - 1;   
}
int get_aft(int pos) {
	// 求 >= pos 的个数.   
	return n - get_pre(pos - 1);  
}
int main() {
	// setIO("input"); 
	scanf("%d%d",&n,&K);  
	for(int i=1;i<=n;++i) {
		scanf("%d",&a[i]); 
		B[i] = a[i]; 
		co[a[i]] ++ ;  
		if(co[a[i]] >= K) {
			printf("0\n"); 
			return 0; 
		}
		A[++cnt] = a[i];   
		A[++cnt] = a[i] - 1;  
		A[++cnt] = a[i] + 1; 
	}
	B[n + 1] = 1200000000; 
	sort(B+1, B+1+n);   
	for(int i = n; i >= 1; -- i) 
		suf[i] = suf[i + 1] + 1ll * B[i];   
	for(int i = 1; i <= n ; ++ i) 
		pre[i] = pre[i - 1] + 1ll * B[i];   
	sort(A+1, A+1+cnt);  
	ll ans = 10000000000000000ll;
	for(int i=1;i<=cnt;++i) {
		// 计算 A[i] 的答案.   
		int num = co[A[i]];   
		int det = K - num;
		int prfix = get_pre(A[i] - 1);  
		int sufix = get_aft(A[i] + 1);     
		if(prfix >= det || sufix >= det) {
			if(prfix >= det) {
				ll dprfix = 1ll * prfix * (A[i] - 1) - pre[prfix];     
				ans = min(ans, dprfix + det); 
			}
			if(sufix >= det) {
				ll dsufix = suf[n - sufix + 1] - 1ll * sufix * (A[i] + 1);     
				ans = min(ans, dsufix + det); 
			}
		}
		else {
			// 需要都到达目的地.  
			ll dsufix = suf[n - sufix + 1] - 1ll * sufix * (A[i] + 1) ;
			ll dprfix = 1ll * prfix * (A[i] - 1) - pre[prfix];
			ans = min(ans, dsufix + dprfix + det);    
		}
	}
	printf("%lld\n", ans);  
	return 0; 
}