水题,直接安排。
T = int(input())
while T:
T -= 1
n = int(input())
s = set("qwertyuiopasdfghjklzxcvbnm")
for i in range(n):
str = input()
cur = set(str)
s = s & cur
print(len(s))
枚举个答案,然后安排。
import math
import string
def f(n, cnt, list):
ret = 0
lim = n // cnt
if lim > 26:
return n
for i in range(lim):
if list[i] > cnt:
ret += list[i] - cnt
for i in range(lim, 26):
ret += list[i]
return ret
T = int(input())
while T:
T -= 1
s = input()
n = len(s)
ans = n
l = []
for c in string.ascii_uppercase:
l.append(s.count(c))
l.sort(reverse = True)
for i in range(1, int(math.sqrt(n)) + 1):
if n % i == 0:
ans = min(ans, min(f(n, i, l), f(n, n // i, l)))
print(ans)
先把整个坐标的四个角都问一下(其实只用问三个就可以得到第四个),得到它们到矩形的四个角的距离。现在只要定出矩形左边界的x坐标一切就定了。分类讨论一下左边界情况然后问即可。
import sys
def query(x, y):
print("Q %d %d" % (x, y))
sys.stdout.flush()
return int(input())
def answer(x, disSW, disNW, disSE, disNE):
y = disSW - x
yy = x + MAX - disNW
xx = - disNE + 2 * MAX - yy
print("A %d %d %d %d" % (x, y, xx, yy))
sys.stdout.flush()
input()
MAX = 10**9
T = int(input())
while T > 0:
T -= 1
disSW = query(0, 0)
disNW = query(0, MAX)
disSE = query(MAX, 0)
disNE = query(MAX, MAX)
if MAX - disSW >= disNW:
answer(query(0, disSW), disSW, disNW, disSE, disNE)
else:
cx = (disSW + disNW - MAX) >> 1
cy = disSW - cx
answer(cx + query(cx, cy), disSW, disNW, disSE, disNE)
打表发现一定是两种操作交替进行。在偶数的情况下会多guess一次。奇数就是严格两者交替进行。
MOD = 1000000007
def getPow(a, b):
r = 1
while b:
if b & 1: r = r * a % MOD
a = a * a % MOD
b >>= 1
return r
def getInv(x):
return getPow(x, MOD - 2)
T = int(input())
while T:
T -= 1
n, k, m = [int(i) for i in input().split()]
if m % 2:
ans = getPow(getInv(n), (m + 1) >> 1) * getPow(n-1, (m + 1) >> 1) % MOD
else:
ans = getPow(getInv(n), m >> 1) * getPow(n-1, m >> 1) * (n + k - 1) * getInv(n + k) % MOD
print((MOD - ans + 1) % MOD)
f[i][j]表示以i为根的子树里已经割掉了的子树的个数的奇偶性是j的答案,然后安排。
#include
#define N 100005
#define MOD 1000000007
using namespace std;
int n, sum[N], ex, f[N][2], last[N], v[N], ecnt;
struct edge{int next, to;}e[N<<1];
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
void dfs(int x, int fa)
{
f[x][0] = 1;
f[x][1] = 0;
sum[x] = v[x];
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(y == fa) continue;
dfs(y, x); sum[x] ^= sum[y];
int h[] = {0, 0};
if(sum[y] == ex)
{
(h[1] += 1ll * f[x][0] * f[y][0] % MOD) %= MOD;
(h[0] += 1ll * f[x][1] * f[y][0] % MOD) %= MOD;
}
if(sum[y] == 0)
{
(h[0] += 1ll * f[x][0] * f[y][1] % MOD) %= MOD;
(h[1] += 1ll * f[x][1] * f[y][1] % MOD) %= MOD;
}
(h[1] += 1ll * f[x][0] * f[y][1] % MOD) %= MOD;
(h[0] += 1ll * f[x][1] * f[y][1] % MOD) %= MOD;
(h[0] += 1ll * f[x][0] * f[y][0] % MOD) %= MOD;
(h[1] += 1ll * f[x][1] * f[y][0] % MOD) %= MOD;
f[x][0] = h[0];
f[x][1] = h[1];
}
}
int main()
{
cin >> n >> ex;
for(int i = 1; i <= n; i++) cin >> v[i];
for(int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
addedge(x, y);
addedge(y, x);
}
dfs(1, 0);
int ans = 0;
if(sum[1] == ex) (ans += f[1][0]) %= MOD;
if(sum[1] == 0 ) (ans += f[1][1]) %= MOD;
cout << ans << endl;
}
显然的想法是缩强,然后在DAG上记f[i][j]表示做到第i个强连通分量,这时候已经有j个人不给钱的最大收益。转移即强连通分量内按B排序后枚举收多少钱。
分析时间复杂度。显然时间复杂度的重点在于点和边分别会被枚举多少次。
考虑点的时间复杂度。显然每一个联通分量关于点的时间复杂度是独立的。所以如果有最值,那么每一个联通分量取到最值时联通分量里面点的个数都是一样的,不妨设为s,那么时间复杂度不大于 n s × k × s = n k \frac{n}{s} \times k \times s =nk sn×k×s=nk ,所以这没问题。
考虑边的时间复杂度。每一条边最多只会在一个分量里枚举到,且最多枚举到k次,所以边的时间复杂度不大于 m k mk mk,也没有问题。
写的时候边的复杂度懵了一会儿,T了半天,还以为算法假了。冷静一下发现是我菜了…
#include
#define N 200005
#define M 400005
using namespace std;
int n, m, k, last[N], ecnt, b[N];
struct edge
{
int next, to;
}e[M];
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
bool insta[N];
int sta[N], stacnt, dfn[N], low[N], timer, bcnt, bel[N], deg[N];
vector<int> vec[N];
void tarjan(int x)
{
dfn[x] = low[x] = ++timer; sta[++stacnt] = x; insta[x] = 1;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
if(!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
else if(insta[y])low[x] = min(low[x], dfn[y]);
}
if(low[x] == dfn[x])
{
bel[x] = ++bcnt; insta[x] = 0; vec[bcnt].push_back(x);
for(; sta[stacnt] != x; stacnt--)
{
bel[sta[stacnt]] = bcnt, insta[sta[stacnt]] = 0;
vec[bcnt].push_back(sta[stacnt]);
}
stacnt--;
}
}
struct pdge
{
int a, b;
}pe[M];
void build()
{
map<pair<int,int>, bool> vis;
int pecnt = 0;
for(int i = 1; i <= n; i++)
{
for(int j = last[i]; j; j = e[j].next)
{
if(bel[i] != bel[e[j].to] && !vis[make_pair(bel[i], bel[e[j].to])])
{
pe[++pecnt] = (pdge){bel[i], bel[e[j].to]};
vis[make_pair(bel[i], bel[e[j].to])] = true;
deg[bel[e[j].to]]++;
}
}
}
ecnt = 0;
for(int i = 1; i <= n; i++)
last[i] = 0;
for(int i = 1; i <= pecnt; i++)
{
addedge(pe[i].a, pe[i].b);
}
}
long long f[N][205];
bool cmp(int x, int y){return b[x] < b[y];}
int main()
{
int T; cin >> T;
for(; T--; )
{
ecnt = timer = bcnt = 0;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++)
{
last[i] = dfn[i] = low[i] = 0;
vec[i].clear();
deg[i] = 0;
cin >> b[i];
}
for(int i = 1; i <= m; i++)
{
int a, b; cin >> a >> b;
addedge(a, b);
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
// cout << timer << endl;
build();
queue<int> q;
bcnt++;
for(int i = 1; i < bcnt; i++)
{
addedge(i, bcnt);
if(deg[i] == 0) q.push(i);
}
for(int i = 1; i <= bcnt; i++)
for(int j = 0; j <= k; j++)
f[i][j] = 0;
for(; !q.empty(); )
{
int x = q.front(); q.pop();
sort(vec[x].begin(), vec[x].end(), cmp);
int cnt = vec[x].size();
long long tmp[205] = {0};
for(int i = 0; i < cnt; i++)
for(int j = 0; j <= k && (i + j) <= k; j++)
tmp[i + j] = max(tmp[i + j], f[x][j] + 1ll * (cnt - i) * b[vec[x][i]]);
for(int j = 0; j <= k; j++)
for(int t = last[x]; t; t = e[t].next)
f[e[t].to][j] = max(f[e[t].to][j], max(f[x][j], tmp[j]));
for(int t = last[x]; t; t = e[t].next)
{
deg[e[t].to]--;
if(!deg[e[t].to]) q.push(e[t].to);
}
}
long long ans = 0;
for(int j = 0; j <= k; j++)
{
ans = max(ans, f[bcnt][j]);
}
cout << ans % 1000000021 << endl;
deg[bcnt] = 0;
vec[bcnt].clear();
}
}
首先考虑能否对于每一个点,直接找到它的第Ki大的距离。估计要用数据结构,但好像没有合适的数据结构。
接着考虑能不能对每一个点二分答案,现在只需要在大概 O ( log n ) O(\log n) O(logn)的时间算出和一个点距离小于等于一个给定值的点的数量。如果这个点是根,那直接遍历。但显然根只能定一个,试试树分治。对于一个点,不断回跳它在树分治上的父亲统计答案即可。
#include
#define N 200005
using namespace std;
int last[N], ecnt = 1;
struct edge{int next, to;}e[N<<1];
void addEdge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
int n, k[N], *pCnt[N], *eCnt[N], from[N], dis[30][N], siz[N], conSiz[N], conSizEdge[N], dep[N];
bool vis[N];
void make(int *a, int siz)
{
for(int i = 1; i < siz; i++) a[i] += a[i-1];
}
void findCG(int x, int fa, int &cg, int totSiz)
{
siz[x] = 1; bool isCG = true;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(vis[y] || y == fa) continue;
findCG(y, x, cg, totSiz); siz[x] += siz[y];
if(siz[y] > totSiz / 2) isCG = false;
}
if(totSiz - siz[x] > totSiz / 2) isCG = false;
if(isCG) cg = x;
}
void dfs(int x, int fa, int *pc, int *ec, int di, int fr, int *dis)
{
pc[di]++; ec[di]++;
from[x] = fr; dis[x] = di;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(vis[y] || y == fa) continue;
dfs(y, x, pc, ec, di + 1, fr, dis);
}
}
int getSiz(int x, int fa)
{
int ret = 1;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(vis[y] || y == fa) continue;
ret += getSiz(y, x);
}
return ret;
}
void DC(int x, int totSiz, int d)
{
findCG(x, 0, x, totSiz);
conSiz[x] = totSiz;
dep[x] = d;
vis[x] = 1;
pCnt[x] = new int[totSiz]();
pCnt[x][0] = 1;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(vis[y]) continue;
siz[y] = getSiz(y, x);
eCnt[i] = new int[siz[y] + 1]();
dfs(y, x, pCnt[x], eCnt[i], 1, i, dis[d]);
make(eCnt[i], siz[y] + 1);
conSizEdge[i] = siz[y] + 1;
}
make(pCnt[x], totSiz);
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(vis[y]) continue;
DC(y, siz[y], d + 1);
}
}
int calc(int xx, int dd)
{
int ret = 0, x = xx, d = dd, curDep = dep[x];
while(true)
{
if(d >= 0)
ret += pCnt[x][min(d, conSiz[x] - 1)];
if(from[x])
{
curDep--;
d = dd - dis[curDep][xx];
if(d >= 0)
ret -= eCnt[from[x]][min(d, conSizEdge[from[x]] - 1)];
x = e[from[x] ^ 1].to;
}
else break;
}
return n - ret;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> k[i];
for(int i = 1; i < n; i++)
{
int a, b; cin >> a >> b;
addEdge(a, b); addEdge(b, a);
}
DC(1, n, 0);
for(int i = 1; i <= n; i++)
{
int l = 0, r = n;
for(; l < r; )
{
int mid = (l + r + 1) >> 1;
if(calc(i, mid) >= k[i]) l = mid;
else r = mid - 1;
}
cout << l << " \n"[i == n];
}
}