前言:
长春比赛没几天了,熟悉浙大的出题风格,就要从AK几乎浙大月赛开始。加油!
A
说实话只是知道方向,具体的想不清楚。老婆喊了一句——从右往左第一个重复的,于是就直接想到线段树解法了。
离线操作——按照尾巴排序,更新中间重复的段落。
存疑 :如果是从左往右,咋办。。。
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define LL int
const int maxn = 600000;
LL add[maxn<<2];
LL sum[maxn<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
if (add[rt]) {
add[rt<<1] = add[rt];
add[rt<<1|1] = add[rt];
sum[rt<<1] = add[rt];
sum[rt<<1|1] = add[rt];
add[rt] = 0;
}
}
void build(int l,int r,int rt) {
add[rt] = 0;
if (l == r) {
//scanf("%lld",&sum[rt]);
sum[rt] = 0;
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
if (L <= l && r <= R) {
add[rt] = c;
sum[rt] = c;
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
LL query(int p,int l,int r,int rt) {
if (l == r) {
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (p <= m) return query(p , lson);
else return query(p , rson);
}
map<int , int> Meng;
map<int , int> ::iterator iter;
const int N = 1000000;
int nex[N];
int n , a[N] , Q;
int ans[N];
struct ZHU{
int l , r , id;
bool operator < (const ZHU & A) const{
return r < A.r;
}
void input(){
scanf("%d%d", &l , &r);
}
}io[N];
void solve(){
Meng.clear();
memset(nex , -1 , sizeof(nex));
for (int i = 1 ; i <= n ; ++i){
scanf("%d" , &a[i]);
}
for (int i = n ; i >= 1 ; --i){
iter = Meng.find(a[i]);
if (iter != Meng.end()){
nex[i] = iter->second;
}
Meng[a[i]] = i;
}
build(1 , n , 1);
memset(ans , 0 , sizeof(ans));
scanf("%d", &Q);
for (int i = 1 ; i <= Q ; ++i){
io[i].input();
io[i].id = i;
}
sort(io + 1 , io + 1 + Q);
int j = 1;
for (int i = 1 ; i <= Q; ++i){
while (j <= io[i].r){
if (nex[j] == -1) {
++j;
continue;
}
//printf("Update %d: %d\n",i,pre[j]);
update(nex[j] , n , j , 1 , n , 1);
j++;
}
int res = query(io[i].r , 1 , n , 1);
if (res >= io[i].l) ans[io[i].id] = res;
else ans[io[i].id] = 0;
}
for (int i = 1 ; i <= Q ; ++i){
if (ans[i] == 0) printf("OK\n");
else printf("%d\n",a[ans[i]]);
}
printf("\n");
}
int main(){
while (cin >> n) solve();
}
B
貌似就是一个很水的贪心,Bella 648B 就过了。表示困了没法看。。。
C
记得是树状数组+二分,NlogNlogN,吴宏给过NlogN的算法,一直没来得及看。。。罪过罪过。
int n , m ;
const int N = 60000;
int a[N] , b , tree[N];
void add(int p){
for (int i = p ; i <= n ; i += low_bit(i))
tree[i] += 1;
}
int getsum(int p){
int ret = 0;
for (int i = p ; i >= 1 ; i-= low_bit(i))
ret += tree[i];
return ret;
}
int ans[N];
void solve(){
memset(tree , 0 , sizeof(tree));
for (int i = 1 ; i <= n ; ++ i){
scanf("%d", &a[i]);
b = a[i];
int low = 1, high = n , ret = n , mid;
do{
int mid = (low + high) >> 1;
if (mid - getsum(mid) >= b){
ret = min(ret , mid);
high = mid - 1;
}
else low = mid + 1;
}while (low <= high);
ans[i] = ret;
add(ret);
}
scanf("%d",&m);
for (int i = 1 ; i <= m ; ++i){
scanf("%d",&b);
if (i > 1) printf(" ");
printf("%d",ans[b]);
}
printf("\n");
}
int main(){
while (cin >> n) solve();
}
吴宏的NlogN线段树:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
int a[60000];
struct node{
int l,r,num;
}tree[300000];
int ans,now;
int n,m,man;
void build(int i,int left,int right)
{
node &p=tree[i];
p.l=left;
p.r=right;
if (left==right)
{
p.num=1;
return;
}
int mid=(left+right)>>1;
build(i<<1,left,mid);
build(i<<1|1,mid+1,right);
p.num=tree[i<<1].num+tree[i<<1|1].num;
}
void update(int i,int pos)
{
node &p=tree[i];
if (p.l==p.r)
{
p.num=0;
return;
}
int mid=(p.l+p.r)>>1;
if (pos<=mid)
update(i<<1,pos);
else
update(i<<1|1,pos);
p.num=tree[i<<1].num+tree[i<<1|1].num;
}
void search(int i)
{
node &p=tree[i];
if (p.l==p.r)
{
ans=p.l;
return;
}
if (tree[i<<1].num>=now)
{
search(i<<1);
}
else
{
now-=tree[i<<1].num;
search(i<<1|1);
}
}
int main()
{
// freopen("in.txt","r",stdin);
while (cin>>n)
{
build(1,1,n);
for (int k=1;k<=n;k++)
{
scanf("%d",&now);
ans=0;
search(1);
a[k]=ans;
update(1,ans);
}
cin>>m;
for (int k=1;k<m;k++)
{
scanf("%d",&man);
printf("%d ",a[man]);
}
scanf("%d",&man);
printf("%d\n",a[man]);
}
return 0;
}
F
数据一直有问题。。。鄙视- -
第一次会做这种题目,记得上次简单听了算法是复旦校赛。
如果n小,并且还是有 > 和 < 的话,那么>的直接减,< 的容斥原理 + 状压dp。
如果n大,dp[i][j] 二维数组dp就可以了吧。。不过m一定要小。。。
int MM[20];
LL OP[1 << 20];
const int MMM = 1 << 20;
void init(){
for (int i = 0 ; i < 20 ; ++i)
MM[i] = (1 << i);
memset(OP , 0LL , sizeof(OP));
for (int i = 0 ; i < (MMM) ; ++ i){
int ii = i;
while (ii){
if (ii & 1) OP[i] ++;
ii>>=1;
}
if (OP[i] & 1LL) OP[i] = -1LL;
else OP[i] = 1LL;
}
}
#define typec long long
const LL modo = 100000007LL;
typec extendGCD(typec a, typec b, typec& x, typec& y)
{
if(!b) return x = 1, y = 0, a;
typec res = extendGCD(b, a % b, x, y), tmp = x;
x = y, y = tmp - (a / b) * y;
return res;
}
typec combinationModP(typec n, typec k, typec p)
{
//if (k == 0) return 0;
if (n < 0 || k < 0) return 0LL;
if(k > n) return 0LL;
if(n - k < k) k = n - k;
typec a = 1LL, b = 1LL, x, y;
LL pcnt = 0LL;
for(int i = 1; i <= k; i++)
{
x = n - i + 1, y = i;
while(x % p == 0) x /= p, pcnt++;
while(y % p == 0) y /= p, pcnt--;
x %= p, y %= p, a *= x, b *= y;
b %= p, a %= p;
}
if(pcnt) return 0;
extendGCD(b, p, x, y);
if(x < 0) x += p;
a *= x, a %= p;
return a;
}
int use[MMM];
char str[1000];
char s1[150],s2[150],s3[150],s[1000];
int n , m , op;
vector<int> Meng;
int x;
void solve(){
//cout << combinationModP(0,0,modo) << endl;
//cout << combinationModP(2,0,modo) << endl;
Meng.clear();
gets(s);
while(1)
{
int d;
if(!gets(s)) break;
if(strlen(s)<2) break;
sscanf(s,"%s %s %s %d",s1,s2,s3,&d);
if(s2[0]=='g') m-=(d+1);
else Meng.push_back(d);
}
if (m < 0){
printf("0\n");
return;
}
int nn = (int)Meng.size();
for (int i = 0 ; i < MM[nn] ; ++ i){
use[i] = 0;
int ii = i;
for (int j = 0 ; j < nn && ii; ++ j){
if (ii & 1) use[i] += Meng[j];
ii >>= 1;
}
}
//LL ans = combinationModP((LL)(m + n - 1) ,(LL)(n - 1) , modo);
//cout << ans << endl;
LL ans = 0;
for (int i = 0 ; i < MM[nn] ; ++i){
if (m < use[i]) continue;
LL op = OP[i];
LL res = combinationModP((LL)(m - use[i] + n - 1),(LL)(n - 1), modo);
//cout << i << " " <<res << endl;
ans = (ans % modo + (op * res + modo)%modo ) % modo;
}
//printf("%lld\n",ans);
printf("%lld\n",ans);
}
int main(){
init();
while (~scanf("%d%d",&n,&m) && n) solve();
}
/*
a: greater than 0
*/
H
如果那时候能冷静一点儿,不是那么浮躁的话是能想出来的。。。很水的记忆化搜索 + dp。按照题目叙述的做就成了。
const int N = 30000;
int c[N] , n , f;
double t[N];
double dp[N];
bool vis[N];
const double MM = (sqrt(5.0) + 1.0) / 2.0;
double dfs(int nowf){
if (vis[nowf]) return dp[nowf];
vis[nowf] = 1;
for (int i = 1 ; i <= n ; ++i){
if (nowf > c[i]) dp[nowf] += t[i];
else dp[nowf] += dfs(nowf + c[i]) + 1.0;
}
dp[nowf] /= (double)n;
return dp[nowf];
}
void solve(){
for (int i = 1 ; i <= n ; ++i){
cin >> c[i];
t[i] = floor(MM * sqr((double)c[i]) + 1e-8);
}
memset(dp , 0 , sizeof(dp));
memset(vis , 0 , sizeof(vis));
printf("%.3lf\n",dfs(f));
}
int main(){
while (cin >> n >> f) solve();
}
I
并查集 + STL
记住union_set的用法
string str;
const int N = 100001;
int n , m , k , x;
set<int> info[N];
namespace ufset{
int fa[N],rank[N];
void init() { for (int i=0;i<N;++i) fa[i]=i,rank[i]=0; }
int find(int x){
int r=x,y;
while (fa[r]!=r) r=fa[r];
while (fa[x]!=r) { y=fa[x],fa[x]=r,x=y;}
return r;
}
void unionset(int x,int y){ // x,y roots
if (rank[x]>rank[y]) fa[y]=x;
else { fa[x]=y; if (rank[x]==rank[y]) ++rank[y]; }
}
};
map<string , int> name;
map<string , int> :: iterator iter;
void solve(){
m = 0;
name.clear();
ufset::init();
while (n --){
cin >> str;
if (str[0] == 'a'){
cin >> str;
int posi;
iter = name.find(str);
if (iter == name.end()){
name[str] = ++m;
info[m].clear();
posi = m;
}
else posi = ufset::find(iter -> second);
scanf("%d",&k);
while (k--){
scanf("%d",&x);
info[m].insert(x);
}
}
else if (str[0]=='c'){
cin >> str;
int posi = name[str];
int fa = ufset::find(posi);
printf("%d\n",info[fa].size());
}
else{
int p1 , p2;
cin >> str ; p1 = name[str];
cin >> str ; p2 = name[str];
p1 = ufset::find(p1);
p2 = ufset::find(p2);
if (ufset::find(p1) != ufset::find(p2))
ufset::unionset(p1 , p2);
int fa = ufset::find(p1);
set_union(info[p1].begin() , info[p1].end() , info[p2].begin() , info[p2].end() , inserter(info[fa] , info[fa].begin()));
}
}
}
int main(){
while (cin >> n) solve();
}
K
栈模拟 + KMP 。 其实不用KMP。。。
int next[1005];
stack<int> r;
char a[1005];
char s[812000];
int n,len_a,len_s;
void Get_Next()
{
a[0] = '#';
len_a = strlen(a);
len_s = strlen(s);
next[1] = 0;
int j = 0;
for(int i=2;i<=len_a;i++)
{
while(j > 0 && a[j+1] != a[i])
j = next[j];
if(a[j+1] == a[i])
j++;
next[i] = j;
}
}
int ans;
void Str_Match()
{
int i = 0,j = 0;
int last = -521000;
while(i < len_s)
{
while(j>0 && a[j+1]!=s[i])
j = next[j];
if(a[j+1] == s[i])
{
j++;
}
r.push(j);
if(j == len_a - 1){
j=next[j];
ans++;
for (int k = 1; k < len_a; ++k)
r.pop();
if (!r.empty())
{
i++;
j = r.top();
}else
{
i++;
j = 0;
}
}else i++;
}
}
int main()
{
while(scanf("%s%s",a+1,s) != EOF)
{
ans = 0;
Get_Next();
Str_Match();
printf("%d\n",ans);
}
return 0;
}
总结:
一个人单挑总是有一点儿力不从心。。。赛后AK的动力不足,这不行啊。。。
每次做题到最后没劲儿了就喜欢抱着老婆想,之后就能很踏实地搞出1~2个题目,要克服这个啊- -自己要淡定出题。
金华 + 杭州加油!